字符串的最小表示法

时间:2022-07-05 22:13:08

给定一个字符串,从其中一个点开始遍历循环遍历回这个点,如果他的序列比所有这么做获得的字符串的字典序都小,那么他就是这个字符串的最小表示。

比如abba,可以变为bbaa,baab,aabb,其中aabb就是他的最小表示方法。

那么怎么实现这个方法用O(n)来实现呢?先给出代码:

 1 public int res(String c,int len) {
 2         int i=0,j=1,k=0;
 3         while(i<len&&j<len&&k<len) {
 4             if(c.charAt((i+k)%len)==c.charAt((j+k)%len))
 5                 k++;
 6             else if(c.charAt((i+k)%len)<c.charAt((j+k)%len)) {
 7                 j=j+k+1;
 8                 k=0;
 9             }
10             else {
11                 i=j+k;
12                 j=i+1;
13                 k=0;
14             }
15         }
16         return i;
17     }

其中也可以把将字符串c用toCharArray()方法转换成char数组来操作,会节约时间。

代码中的i、j、k可以理解为:

i:当前字符串最小表示位置。

j:和当前最小表示位置比较位置。

k:i和j位置后第k个字符。

在while循环中,如果 i+k 位置字符和 j+k 位置字符相同,那么k++,也就是比较下一个位置字符串的字典序,比如:aabaac,初始i=0,j=3,k=0,相同,k++后依旧相同,再下一个位置b和c就可以比较出大小了。

那么当 i+k 位置字符字典序大于 j+k 呢?比如:bba,b的字典序大于a,那么很明显他的遍历子串中以a开头的字串肯定比b开头的字串字典序要小。所以直接把 i 的位置移到 j ,然后让 j 变为下一个位置也就是 i+1 就可以了,注意这个时候你应该已经执行了 i=j 的操作!然后要重置 k。

那么当 i+k 位置字符字典序小于 j+k 呢?很多小伙伴应该不明白为什么是 j=j+k+1,因为按照咱们呢的思维定式应该从 j 的下一位开始比较啊?

比如:babade

首先假设 i=0,j=2,k=1,很明显这个时候 i->i+k 和 j->j+k 截取的字串是一样的,k++后你发现 b小于d,这个时候怎么可以从e开始遍历呢?明显3位置的a要小于b啊??

3位置a小于0位置b是没有错误的,但是很明显的是不存在 i=0,j=2,k=1这种情况的,因为在 i=0,j=1的时候,i 就应该变为 1 了,也就是如果 j>i+k的话,i->i+k的所有字符都应该大于 i 的。

那如果 j<i+k 呢?首先可以明确 i->j 区间的字符都不可能比 i 小(毕竟 j 已经遍历过去了),那么当 i+k=j 的时候,字符串 j+k 和区间 i+k的字符串是相同的,也就是不可能能小于 i 喽,所以直接把 j 的位置挪到 j+k+1 就可以啦。(ps:j 到 j+k区间不可能存在一个节点 n 使得他开头的字符串小于 i 位置,因为 n -> j+k-1 的字串如果和 i -> i+k-1-n的字串相同的话,第 j+k 也依旧大于 i+k 位置的字符)

至于while循环的条件为什么是 i<len&&j<len&&k<len 而不是 i+k<len和 j+k<len,大家如果明白了我上面说的自然也就明白了。

希望我说的使得大家豁然开朗!如果还有不明白的欢迎评论以及留言!