题目
思路
看到这道题目我脑子里面第一个闪过的是KMP,但是看到第二问我就发现竟然是我不会的最小表示法。
首先明确一个思路,如果对于两个东西我们要确定是否相同,最好的方法就是确定一个最小的东西判断相等,例如在AcWing 156. 矩阵 中就是最小的浏览顺序,而这里则是最小的字典序,所以我们不难想到最小表示法。
什么?你说我总是讲的不好,我也没打算讲啊,这个算法OIwiki讲的挺好的,我就只做一些注释吧以及答疑吧。
首先,暴力是我们每次比较(i)和(j)开始的循环同构,把当前比较到的位置记作 ,每次遇到不一样的字符时便把大的跳过,最后剩下的就是最优解。
int k = 0, i = 0, j = 1;
while (k < n && i < n && j < n) {
if (sec[(i + k) % n] == sec[(j + k) % n]) {
++k;
} else {
if (sec[(i + k) % n] > sec[(j + k) % n])
++i;
else
++j;
k = 0;
if (i == j) i++;
}
}
i = min(i, j);
但是这个很明显在(aaaaa...b)时炸了,这个算法是(O(n^2))的。
但是,我们可以优化!
int k = 0, i = 0, j = 1;
while (k < n && i < n && j < n) {
if (sec[(i + k) % n] == sec[(j + k) % n]) {
k++;
} else {
sec[(i + k) % n] > sec[(j + k) % n] ? i = i + k + 1 : j = j + k + 1;
if (i == j) i++;
k = 0;
}
}
i = min(i, j);
至于正确性吗,没优化的肯定是对的,优化并没有改变正确性,所以也是对的,没有毛病。
问题一
为什么是最后是选择(min(i,j))呢?
首先看看退出的条件,那么在(i,j)跳出(n)的范围是,很明显(min)会选另外一个,但是(k=n)时呢?说明两个都是最小表示,随便选一个都可以。
问题二
(k=n)是个什么情况?
说明同时有两个位置都是最小表示法,那么这个字符串肯定是由一个循环节循环组成的。
有人会问会不会是(n)个循环节加上半个循环节呢?
不会的,你画个图就会发现这样子的话(k)是不可能等于(n)的。
代码
#include<cstdio>
#include<cstring>
#define N 2100000
using namespace std;
char a[N],b[N];
int n;
int zxbsf(char *s)//求最小表示法的位置
{
int i=1,j=2,k=0;
while(i<=n && j<=n && k<n)
{
if(s[i+k]==s[j+k])k++;
else
{
if(s[i+k]>s[j+k])i=i+k+1;
else j=j+k+1;
if(i==j)j++;
k=0;
}
}
return i<=n?i:j;//其实和min大同小异。
}
int main()
{
scanf("%s",a+1);scanf("%s",b+1);n=strlen(a+1);
for(int i=2*n;i>n;i--)a[i]=a[i-n],b[i]=b[i-n];//复制一份
int x=zxbsf(a),y=zxbsf(b);
int t=0;
for(int i=0;i<n;i++)
{
if(a[x+i]!=b[y+i])
{
t=1;
break;
}
}
if(t==1)printf("No
");
else
{
printf("Yes
");
for(int i=0;i<n;i++)printf("%c",a[x+i]);
printf("
");
}
return 0;
}