例题 158
最小表示法的思想是很暴力的,每次两个指针i, j不停移动,对i, j后面的子串进行比较。
设两个后缀的lcp为k。
要不然k=n,有循环节,且i,j不相等,则必经历过一整个循环节,可以退出,i,j均为答案。
否则k<n,不妨设i后缀字典序更大,则i i+k后缀均报废。
因为平移后任意 ii 总有对应 jj比它更优。
所以i+=k+1,跳过一段。
不要看这个优化非常显然就没什么用,实际上他是改变复杂度的关键一步。
没有这一步的话枚举i,j,进行比较总复杂度最坏可以O(n3)级别。
而加上优化后,简单分析可得, 单次操作后,i+j的和增加了至少k。
i+j最大为 O(n) 级别,而k的总和也为O(n)级别,所以总复杂度降为O(n),实现了一个质的飞跃。
代码如下。
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 1e6 + 20;
int n, m;
char a[N << 1], b[N << 1];
int solve(char *s)
{
int i = 1, j = 2;
while(i <= n && j <= n)
{
int k = 0;
while(k < n && s[i + k] == s[j + k]) k ++;
if(k == n) break;
if(s[i + k] > s[j + k]) i += k + 1;
else j += k + 1;
if(i == j) j ++;
}
return min(i, j);
}
int main()
{
scanf("%s%s", a + 1, b + 1);
n = strlen(a + 1);
for(int i = 1; i < n; i ++) a[i + n] = a[i], b[i + n] = b[i];
int x = solve(a);
int y = solve(b);
a[x + n] = b[y + n] = 0;
if(strcmp(a + x, b + y)) puts("No");
else
{
puts("Yes");
printf("%s", a + x);
}
return 0;
}