Jzoj5603 xiz

给定字符串 S 和 T。
串A和串B匹配的定义改为:存在一个字符的映射,使得A应用这个映射之后等于B,且这个映射必须为一个排列。
A=121, B=313,当映射为{1->3, 2->1, 3->2}时A'=B,可以匹配
A=212, B=313,当映射为{1->1, 2->3, 3->2}时A'=B,可以匹配
A=232, B=313,当映射为{1->2, 2->3, 3->1}时A'=B,可以匹配
A=123, B=111,当映射为{1->1, 2->1, 3->1}时A'=B,但此时映射不为一个排列,不能匹配

求 S 的哪些连续子串与 T 匹配.

这是一个非常巧妙的题,不仅仅实在kmp那个部分

定义lst[x]表示最大的y使得y<x且s[y]=s[x] 若不存在则为0

显然,两个字符串可以匹配,当且仅当两个串的lst数组可以匹配

这里“匹配”指的是A[i]=B[j] 或者 A[i]=0且B[j]>=i 因为文本串长过模板

那么我们的KMP也要按照这个方式来写就行了(当然完全可以用哈希)

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int n,m,S[1000010],T[1000010],nt[1000010];
int t,ans[1000010],lst[1000010];
int KMP(){
	scanf("%d%d",&m,&n); t=0;
	memset(lst,-1,sizeof lst);
	for(int x,i=0;i<m;++i){
		scanf("%d",&x);
		if(!~lst[x]) T[i]=0; else T[i]=i-lst[x];
		lst[x]=i;
	}
	memset(lst,-1,sizeof lst);
	memset(nt,0,sizeof nt);
	for(int x,i=0;i<n;++i){
		scanf("%d",&x);
		if(!~lst[x]) S[i]=0; else S[i]=i-lst[x];
		lst[x]=i;
	}
	for(int i=1,j;i<n;++i)
		for(j=i;j;) 
			if(S[j=nt[j]]==S[i] || (S[j]==0 && S[i]>j)){ nt[i+1]=j+1; break; }
	for(int i=0,j=0;i<m;++i){
		if(j<n &&(T[i]==S[j] || (S[j]==0 && T[i]>j))) ++j;
		else while(j){
			j=nt[j];
			if(T[i]==S[j] || (S[j]==0 && T[i]>j)){ ++j; break; }
		}
		if(j==n) ans[++t]=i-n+1;
	}
	printf("%d
",t);
	for(int i=1;i<=t;++i) printf("%d ",++ans[i]); puts("");
}
int main(){
	freopen("xjz.in","r",stdin);
	freopen("xjz.out","w",stdout);
	int T; scanf("%d%*d",&T);
	while(T--) KMP();
}

原文地址:https://www.cnblogs.com/Extended-Ash/p/9477112.html