【概述】
最大最小表示法用于解决字符串的同构问题,其在复杂度为 O(n) 的时间内求出一个字符串的所有同构串中字典序最大(小)的串的起始位置。
应用:
- 给出 n 个循环字符串判断有多少不同字符串:逐个用最大(小)表示法表示,然后加入 set 去重
- 循环字符串所有同构串中字典序最大(小)的表示:用最大(小)表示法求出起始位置,输出即可
- 判断两个字符串是否同构:将两字符串用最大(小)表示法表示,然后逐个字符比较
【算法原理】
设一字符串 S,且 S’ 是 S 的循环同构的串的最小表示,那么对于字符串循环同构的最小表示法,其问题实质是求 S 串的一个位置,从这个位置开始循环输出 S,得到的 S’ 字典序最小。
最朴素的算法是设 i、j 两个指针,i 指向最小表示的位置,j 作为比较指针。
令 i=0,j=1,那么:
- 若 S[i]>S[j],则:i=j,j=i+1
- 若 S[i]<S[j],则:j++
- 若 S[i]=S[j],则设指针 k,分别从 i 和 j 位置向下比较,直到 S[i]!=S[j]
若 S[i+k]>S[j+k],则:i=j,j=i+1
否则 j++
最后返回 i 即可
可以看出,朴素算法在 S[i]=S[j] 时,i 的指针移动的太少了,在遇到像 bbb…bbbbbba 这样复杂的字符串时,时间复杂度可能会退化到 O(n^2),针对这一问题加以改进可得到 O(n) 的最小表示法的算法,其核心思路是在 S[i]=S[j] 时同时维护 i、j 两个指针
同样令 i=0,j=1,那么:
- 若 S[i]>S[j],则:i=j,j=i+1
- 若 S[i]<S[j],则:j++
- 若 S[i]=S[j],则设指针 k,分别从 i 和 j 位置向下比较,直到 S[i]!=S[j]
若 S[i+k]>S[j+k],则:i=i+k
否则 j++
最后返回 i 和 j 的小者即可
【实现】
1.最小表示法
int minmumRepresentation(char *str){//最小表示法
int len=strlen(str);
int i=0;//指向字符串最小的位置
int j=1;//比较指针
int k=0;//str[i]与str[j]相等时一次移动几个
while(i<len&&j<len&&k<len){
int temp=str[(i+k)%len]-str[(j+k)%len];//比较值的长度
if(temp==0)
k++;
else{
if(temp>0)//维护i
i=i+k+1;
else//维护j
j=j+k+1;
if(i==j)//相等时比较指针后移
j++;
k=0;
}
}
return i<j?i:j;//返回i、j中较小的那个
}
2.最大表示法
int maxmumRepresentation(char *str){//最大表示法
int len=strlen(str);
int i=0;//指向字符串最小的位置
int j=1;//比较指针
int k=0;//str[i]与str[j]相等时一次移动几个
while(i<len&&j<len&&k<len){
int temp=str[(i+k)%len]-str[(j+k)%len];//比较值的长度
if(temp==0)
k++;
else{
if(temp>0)//维护i
j=j+k+1;
else//维护j
i=i+k+1;
if(i==j)//相等时比较指针后移
j++;
k=0;
}
}
return i<j?i:j;//返回两者最小值
}