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\n",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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值