牛客NC15879 A Simple Problem
传送门:A Simple Problem
题意
给定两个序列s1和s2,同样的数字可以用相同的别的数字代替(并且也可以是出现过的数字),问s2在s1中出现了几次。
题解
首先预处理一下这两个序列,因为数字的位置是不会变得,所以把每个数字用当前位置和前一次出现的位置的差表示,如果是第一次出现,用-1表示,这里为什么是-1呢,不能比-1更大,但可以更小,不然如果匹配的时候 j=0,那么如果 s[i]=0 这样判断的就是不是该满足的那个条件了,这样是不对的。然后就是kmp匹配了,匹配的判断条件改一下就好了,如果当前匹配上了的长度 j 比当前数字和前一次出现的位置的差( s[i] )小的话,并且当前准备匹配的那个数字( s[j] )是第一次出现,那么是可以匹配上的,相当于是把 s[j] 的数字表示成了s[i] 的数字,这样差就是一样的了。如果 j>=s[i] 的话,说明如果s[i]==s[j] 表示他们和前一次出现的位置距离是相等的,这样也是可以匹配上的。如果匹配不上,那么就 j=nt[i];
代码
1 #include<bits/stdc++.h> 2 #define ull unsigned long long 3 #define ll long long 4 #define pb push_back 5 #define ft first 6 #define sd second 7 #define pii pair<int,int> 8 #define pll pair<ll,ll> 9 using namespace std; 10 11 const int maxn=2e5+10; 12 int s1[maxn],s2[maxn],nt[maxn],pre[maxn],pos2[maxn],pos1[maxn]; 13 14 void getpre(int s[],int pos[],int n,int k) 15 { 16 for(int i=0;i<k;i++) pre[i]=-1; 17 for(int i=0;i<n;i++){ 18 if(pre[s[i]]==-1) pos[i]=-1; 19 else pos[i]=i-pre[s[i]]; 20 pre[s[i]]=i; 21 } 22 } 23 24 void getnt(int s[],int n) 25 { 26 int i=0,j=-1; 27 nt[0]=-1; 28 while(i<n){ 29 if(j==-1||(j<s[i]?s[j]==-1:s[i]==s[j])) i++,j++,nt[i]=j; 30 else j=nt[j]; 31 } 32 } 33 34 int kmp(int s[],int p[],int n,int m) 35 { 36 getnt(p,m); 37 int i=0,j=0,ans=0; 38 while(i<n){ 39 if(j==-1||(j<s[i]?p[j]==-1:s[i]==p[j])) i++,j++; 40 else j=nt[j]; 41 if(j==m) ans++; 42 } 43 return ans; 44 } 45 46 int main() 47 { 48 int n,k,m; 49 scanf("%d%d",&n,&k); 50 for(int i=0;i<n;i++) scanf("%d",&s1[i]); 51 scanf("%d",&m); 52 for(int i=0;i<m;i++) scanf("%d",&s2[i]); 53 getpre(s1,pos1,n,k); 54 getpre(s2,pos2,m,k); 55 printf("%d\n",kmp(pos1,pos2,n,m)); 56 return 0; 57 }