学习一下最小表示法……,部分内容与此题无关。
1.起始问题 :比较两个字符串是否循环同构
比如说abca和caab是循环同构的(abca和acba不是循环同构)
2.复杂度较高的算法
例举A串的所有循环同构串 和B串作比较:abca,bcaa,caab,aabc挨个和caab比一边
复杂度O(N^2)
能否优化到O(N)? kmp算法显然可以,但是kmp是针对匹配的,不是专门针对同构的,显得有些麻烦
3.最小表示法,就是充分利用循环同构的特点,进行比较的一种算法,两个串如果循环同构,会有什么特点?
比如以上例举的四个abca的循环同构串abca,bcaa,caab,aabc,如果有一种映射,这四个字符串对应的映射值相同,那比较起来就太快了
我们利用字符串的字典序,把这四个都映射到aabc,那就可以很快比较了,具体还有一些细化,利用最小表示的起始位置等等
具体的算法和证明,
周源2003年国家队论文写的不错
4.最小表示的最靠近串首的起始位置计算
abca的同构串中aabc最小,那么要记录下aabc开头的a在abca中的位置,就不用记录abca了,所以只要算出这个位置,就可以进行字符串之间循环同构的比较
复杂度O(N)
我参考了这篇博客http://blog.csdn.net/zy691357966/article/details/39854359
对于一个字符串,i开始指向0,j开始指向1,算法的过程中,i和j中小的数表示当前认为的合理位置,大的数表示从这个数开始可能会找到更合理的位置
(开始认为从最开始的位置是合理的 i= 0,只有在0后面才可能找到更合理的,所以j=1)
然后把字符串复制一遍到末尾(方便循环)
每次从i+0和j+0开始比较,如果一直比较串长度个,都一样,那么恭喜你,i和j中的最小者就是答案了(为什么自己想(实际上是我懒,要结合i和j所代表的意义))
如果比较到i+k和j+k不相等,结束循环 。
【下面这段是关键】
s(i,i+k-1)的每一位和s(j,j+k-1)的每一位对应相等,而如果s[i+k] > s[j+k],则i到s(i+x,i+k)显然比s(j+x,j+k)大,(0<=x<=K)
那么最小表示的开始位置就不可能是i到i+k【s(x,y)表示x到y的字符串】
所以这个时候把i置为i+k+1,类似的,如果s[j+k] > s[i+k]就把j置为j+k+1
【上面那段是关键】
如果某一次加完之后i和j相等,那就和开始的时候一样,不好比较了,这个时候把i或者j+1,继续算法
每一次这样的循环结束后,如果i或者j超出m的范围了,那么另外一个就是解了!
上一下自己的代码……
#include
#include
#include
#include
using namespace std;
int minpre(char s[])
{
int m = strlen(s);
memcpy(s + m, s,m*sizeof(char));
int i = 0,j = 1;//i表示从i往后是有可能的,j表示下一个试探的位置
while(j < m && i < m)
{
int k;
for (k = 0;s[i + k] == s[j + k] && k < m; k++)
if(k == m ) return min(i,j);//如果相等了,那最小的就是答案
if(s[i + k] > s[j + k]) i = i + k + 1;//i到i+k开始的都不可能
else (j = j + k + 1);//同上
if (i == j) j++;//刚好相等,重新比较
}
if(i < m) return i;
else return j;
}
int main()
{
char s[20005];
int n;
scanf("%d",&n);
while(n--)
{
scanf("%s",s);
printf("%d\n",minpre(s) + 1);
}
}
第一次发博客,小紧张……有点捉急