Description
Input
第一行:包含一个整数N。
第二行:包含一个长度为N的字符串,字符串中只包含小写字母。
第三行:包含一个长度为N的字符串,字符串中只包含小写字母。
Output
输出答案只包含一个数字L,表示圆环最大可能有的格子数。
Sample Input
输入1:
5
abcdx
cdabz
输入2:
4
abcd
cdab
Sample Output
输出1:
4
输出2:
4
Data Constraint
对于20% 的数据,1 <= N <= 5,000
对于50% 的数据,1 <= N <= 600,000
对于100% 的数据,1 <= N <= 2,000,000
Solution
简化题意
如果答案为L,那么第一个字符串的前L个和第二个字符串的前L个循环同构,最大化L
显然可以枚举L,但不是最优
循环同构,那么假设第一个字符串被分成了两部分,把后面的部分拼到前面就和第二个字符串相同,那么可以枚举这个切断的位置i
合法就是s[i~l]=t[1~l-i+1],s[1~i-1]=t[l-i+1~l]
这是什么?
一个字符串的从某个位置开始于另一个字符串的前缀完全相同的最大长度
这就是EXKMP
对于两个字符串,互相求出extend,记为extenda[]和extendb[]
那么只需要extendb[extenda[i]]>i就满足条件了,画个图就可以理解
Code
#include<cstdio>
#include<algorithm>
#include<cstring>
#define N 2010000
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define cl(a) memset(a,0,sizeof(a))
using namespace std;
char s[N],t[N];
int next[N],n,extend[N],b[N];
struct node{
int x,i;
}a[N];
void getextend()
{
next[1]=n;
int a=0,p=0;
fo(i,2,n)
{
int j=next[i-a+1];
if(i+j>p) for(j=max(0,p-i);i+j<=n && t[i+j]==t[j+1];j++);
next[i]=j;
if(i+j-1>p) a=i,p=i+j-1;
}
a=p=0;
fo(i,1,n)
{
int j=next[i-a+1];
if(i+j>p) for(j=max(0,p-i+1);i+j<=n && s[i+j]==t[j+1];j++);
extend[i]=j;
if(i+j-1>p) a=i,p=i+j-1;
}
}
bool cnt(node x,node y){return x.x<y.x;}
int main()
{
scanf("%d\n",&n);
scanf("%s\n",s+1);
scanf("%s\n",t+1);
getextend();
fo(i,1,n) b[i]=extend[i];
swap(s,t);
cl(extend);cl(next);
getextend();
int ans=0;
fo(i,1,n)
{
int j=b[i];
if(extend[j+1]>=i-1) ans=max(ans,i+j-1);
}
printf("%d",ans);
}