字符串匹配
【题目描述】
对于一个字符集大小为C的字符串pp,可以将任意两个字符在p中的位置进行互换,例如p=12321,交换1、21、2得到21312,交换1、4得到42324,交换可以进行任意次。若交换后p变成了字符串q,则成q与p是匹配的。
给定两个字符集大小为C的字符串s、t,求出s中有多少个连续子串与t匹配。
【输入】
第一行两个整数T、C,分别表示数据组数和字符集大小,字符用1∼C的整数来表示。
对于每组数据:第一行两个整数n、m,分别表示s、t的长度。
第二行n个正整数表示s。
第三行m个正整数表示t。
【输出】
对于每组数据,输出包括两行:
第一行一个正整数k,表示s中有k个连续子串与t匹配。
第二行从小到大输出k个数,表示s中与t匹配的连续子串的首位下标(下标从1开始)。
【输入样例】
3 3
6 3
1 2 1 2 3 2
3 1 3
6 3
1 2 1 2 1 2
3 1 3
6 3
1 1 2 1 2 1
3 1 3
【输出样例】
3
1 2 4
4
1 2 3 4
3
2 3 4
【数据规模及约定】
对于10%的数据,满足n,m,C≤1000n,m,C≤1000;
对于另外20%的数据,满足n,m≤105,C≤40n,m≤105,C≤40;
对于另外30%的数据,满足n,m,C≤105n,m,C≤105;
对于100%的数据,满足1≤n,m,C≤106,T=31≤n,m,C≤106,T=3。
【分析】
这其实就是一道KMP的题
题目的难点在于如何交换字符
我们可以开一个数组l[1~c]
l[x]表示上一个x出现的位置
a[i]表示字符s[i]离上一个相同字符出现的距离
b[i]表示字符t[i]离上一个相同字符出现的距离
然后就是KMP
难点是如何判断s[i](t[i])的上一个相同字符是否在模式串之外
这其实很简单
直接判断上一个x出现的距离是否大于j不就行了
于是开两个函数
inline int jdg(int x,int l){return x<l?x:0;}//判断s[i](t[i])的上一个相同字符是否在匹配范围内 //是就返回a[i](b[i]),否就返回0 inline bool eq(int x,int y,int l){return jdg(x,l)==jdg(y,l);}//判断是否匹配
经历千辛万苦
【AC代码】
#include<cstdio> #include<cstring> #include<algorithm> #define N (1000000+2) #define C (1000000+2) using namespace std; int p[N]; int a[N],b[N]; int l[C];//这里很重要,我把C开小了交了不知多少次都没有过 int ans[N]; template<typename T>inline void read(T& x){ char temp=getchar();bool u=0; for(x=0;temp<'0'||temp>'9';u=temp=='-',temp=getchar()); for(;temp>='0'&&temp<='9';x=x*10+temp-'0',temp=getchar()); if(u)x=-x; return ; }//快读 inline int jdg(int x,int l){return x<l?x:0;}//判断s[i](t[i])是否在匹配范围内 //是就返回a[i](b[i]),否就返回0 inline bool eq(int x,int y,int l){return jdg(x,l)==jdg(y,l);}//判断是否匹配 void work(){ register int i,j,x; register int n,m; read(n); read(m); memset(a,0,sizeof a); memset(b,0,sizeof b);//有多组数据,一定要初始化 memset(p,0,sizeof p);//否则就会成为某dengzhaoxing之二 memset(l,0,sizeof l); for(i=1;i<=n;i++){ read(x); a[i]=i-l[x];//将a[i]赋值为字符x与上一x之间的距离 l[x]=i; } memset(l,0,sizeof l);//输入s和t之前都要初始化l for(i=1;i<=m;i++){ read(x); b[i]=i-l[x];//将b[i]赋值为字符x与上一x之间的距离 l[x]=i; } for(i=1,j=0;i<m;i++){ while(j&&!eq(b[i+1],b[j+1],j+1)) j=p[j]; if(eq(b[i+1],b[j+1],j+1)) j++; p[i+1]=j; }//KMP初始化p数组 for(x=i=j=0;i<n;i++){ while(j&&!eq(a[i+1],b[j+1],j+1)) j=p[j]; if(eq(a[i+1],b[j+1],j+1)) j++; if(j==m){ ans[++x]=i+2-m; j=p[j]; } }//KMP匹配 printf("%d ",x); for(i=1;i<=x;i++) printf("%d ",ans[i]);//输出答案 putchar(' '); return ; } int main(){ register int t,c,i; read(t); read(c); for(i=1;i<=t;i++) work(); return 0; }