首先讲一下字符串的循环表示,下面的内容转自:最小表示法
“ 最小表示法”比起动态规划、贪心等思想,在当今竞赛中似乎并不是很常见。但是在解决判断“同构”一类问题中却起着重要的作用。
本文即将讨论字符串中的同构问题,如何巧妙地运用最小表示法来解题呢,让我们继续一起思考吧。
到底什么是循环同构的字符串呢?
直接举个例子:
比如 str1 = "abdea" 而字符串str2是str1从第i(i>0)个开始将str1从i循环到末尾再从头开始循环到i,那么这两个字符串就是循环同构的.比如str2 = "deaab";
那么什么是字符串的最小表示呢?
比如上面那个str1,就是从str1的同构字符串中字典序最小的,那么str1的最小表示就是"aabde".
如何得到一个字符串的最小表示?
设函数 M(s) 返回值意义为:
从 s 的第 M(s) 个字符引起的 s 的一个循环表示是 s 的最小表示。
若有多个值,则返回最小的一个
比如M("bbbaab") = 4; 也就是最小表示为aabbbb,是从字符串"bbbaab"的第4个的字符开始的同构字符串。
现在换一种思路:
设有字符串s1,s2
设 u=s1+s1(也就是将s1复制一份到s1后面) , w=s2+s2 并设指针 i,j 指向 u,w 第一个字符
如果 s1 和 s2 是循环同构的,那么当 i,j 分别指向 M(s1),M(s2) 时,一定可以得到 u[ i→i +|s1|-1]=w[ j→j +|s2|-1] ,迅速输出正确解。
同样 s1 和 s2 循环同构时,当 i,j 分别满足
i ≤ M (s1), j ≤ M (s2) 时,
两指针仍有机会达到 i =M(s1),j=M(s2) 这个状态。
问题转化成,两指针分别向后滑动比较,如果比较失败,如何正确的滑动指针,新指针 i ’,j’ 仍然满足
i ’≤M(s1), j’≤M (s2)
设指针 i,j 分别向后滑动 k 个位置后比较失败 (k ≥ 0) ,即有
u[ i+k ] ≠ w[ j+k ]
设 u[ i+k ]>w[ j+k ] ,同理可以讨论 u[ i+k ]<w[ j+k ] 的情况。
因为 u[x] 在 u[ i ] 后 (x- i ) 个位置,
对应的可以找到在 w[j] 后 (x- i ) 个位置的 w[j+(x- i )] ,
同样对应的有 u[x+1] 和 w[j+(x+1-i)] , u[x+2] 和 w[j+(x+2)- i ] ,
直到 u[i+k-1] 和 w[j+k-1] 。
它们都是相等的,
即有 u[x→i+k-1]=w[j+(x- i )→j+k-1] 。
很容易就得到 u[ x→i+k ]>w[j+(x- i )→ j+k ] 。
所以 s1 (x-1) 不可能是 s1 的最小表示!
因此 M(s1)> i+k ,
指针 i 滑到 u[i+k+1] 处仍可以保证小于等于 M(s1) !
![](http://img0.tuicool.com/fYZVVv.jpg)
同理,当 u[ i+k ]<w[ j+k ] 的时候,可以将指针 j 滑到 w[j+k+1] 处!
也就是说,两指针向后滑动比较失败以后,
指向较大字符的指针向后滑动 k+1 个位置。
举例:
设 s1=‘ babba ’ , s2=‘ bbaba ’ 。(两个同构字符 串)
u=s1+s1='babbababba' w=s2+s2='bbababbaba'
初始化i=0,j=0,k=0;(i作为u的指针,j作为w的指针)
比较失败时 k=1 (u[i+k]!=w [j+k]
由于u[i+k]<w[j+k] 所以j=j+k+1;
j = 2 i不变(i=0); k=0
继续比较发现当k=0时u[i+k]>w[j+k],所以i=i+k+1;
i=1,j不变(j=2);k=0;
继续比较,发现当k=2时u[i+k]>w[j+k]所以i = i+k+1;
i = 4j不变(j=2)k=0;
继续比较,这个时候就找到了最小表示ababb
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<vector>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<algorithm>
using namespace std;
const int maxn=10010;
char s[maxn];
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%s",s);
int p=0,q=1;//取两个同构的字符串一个从下标0开始,一个从下标1开始
int n=strlen(s);
while(p<n&&q<n)
{
int k=0;
for(k=0;k<n;k++)
if(s[(p+k)%n]!=s[(q+k)%n])break;
if(k==n)break;
if(s[(p+k)%n]>s[(q+k)%n])p+=k+1;
else q+=k+1;
if(p==q)q++;
}
printf("%d\n",min(q,p)+1);
}
return 0;
}