牛客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
",kmp(pos1,pos2,n,m));
56     return 0;
57 }
原文地址:https://www.cnblogs.com/lilibuxiangtle/p/13399926.html