给定一个字符串,从其中一个点开始遍历循环遍历回这个点,如果他的序列比所有这么做获得的字符串的字典序都小,那么他就是这个字符串的最小表示。
比如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,大家如果明白了我上面说的自然也就明白了。
希望我说的使得大家豁然开朗!如果还有不明白的欢迎评论以及留言!