【NOI.AC #804】字符串

题目

【NOI.AC #804】字符串_第1张图片

思路

注意到f 相当于是对原来的字符串应用了一个置换p(即f(S)pi = Si),我们把
p 写出来。
考虑p 中的环(如果你不知道这是什么,请参考Day 4 第一题的题目描述),可
以发现应用一次变换相当于是所有字母沿着这个环“走”了一步。
那么我们可以对于每个环,将环上的字母倍长后运行一次KMP 算法,得到所有
的匹配位置。
显然对于某个环而言,这个匹配位置必须是一个等差数列。设首项为t 公差为d,
则我们相当于要求答案mod d 必须为t。
接下来,我们需要证明一个结论,该结论可以很容易地通过打表观察得到:
定理3.1. 所有环长的最小公倍数不超过n。
因为所有环长的最小公倍数不超过n,所以转这么多之后一定会回去,所以如果
有解那么答案不超过n。
于是,我们可以处理出对于每个d 要求的余数md,然后将md,md+d,md+2d, · · ·
打上一个d 的标记,最后第一个有所有d 标记的数字就是答案了。

代码

#include
using namespace std;
int n,sz,maxn,ind;
int in[1000005],at[1000005];
char t[1000005],s[1000005];
vector<int> vec[1000005];
bool vis[1000005],ok[1000005];
bool work(int x) {
	for(int i=2;i<n;i++) if(t[vec[in[i]][(at[i]+x)%vec[in[i]].size()]]!=s[i]) return 0;
	return 1;
}
int main() {
	scanf("%d%s%s",&n,s+1,t+1);
	if(s[1]!=t[1]||s[n]!=t[n]) {
		printf("-1\n");
		return 0;
	}
	for(int i=2;i<n;i++)
		if(!vis[i]) {
			sz++;
			vec[sz].push_back(i);
			in[i]=sz;
			vis[i]=1;
			at[i]=0;
			for(int j=i&1?(i+1)/2:i/2+n/2;j!=i;j=j&1?(j+1)/2:j/2+n/2) {
				vec[sz].push_back(j);
				vis[j]=1;
				in[j]=sz;
				at[j]=vec[sz].size()-1;
			}
		}
	for(int i=1;i<=sz;i++)
		if(vec[i].size()>maxn) {
			maxn=vec[i].size();
			ind=i;
		}
	char ch=s[vec[ind][0]];
	for(int i=1;i<=n;i++) if(t[i]==ch) ok[i]=1;
	for(int i=0;i<maxn;i++)
		if(ok[vec[ind][i]])
			if(work(i)) {
				printf("%d\n",i);
				return 0;
			}
	printf("-1\n");
	return 0;
}

你可能感兴趣的:(题解)