题目意思,给你两个串,让你求出第一个串中有多少个子串,这个子串和第二个串满足:任意一个数字在其中的相对位置都一样。 串与串不能重叠,求最多有多少个。
其实就是给你一个文本串一个模式串求有多少个匹配的问题,典型的KMP,但这里所谓的“匹配”,不是指两个数字相同,而是指两个数字在其中相对位置等价。
由此可以想到,我们可以用一个函数,代替KMP中 s[i]!=p[i]这条语句,而这个函数,就是判断两个数字的相对位置等价。
什么是相对位置?就是这个数字前面有多少数字比它小,还有,前面有多少个数字和它相等。如果两个相对位置都一样,他们就可以匹配。(一定要两者分开讨论,不能直接看<=的数字是否相等,我在这WA了一晚上!!!)。
如何快速求这个数字前面有多少数字比它小呢?首先可以用树状数组求逆序数的思想,求出,由于此题的范围很小,只有25,所以可以用基数排序,统计
<=i位置一共有多少个1,2,3,4...k。这样的话,求小于X的数,就从1累加到X-1就够了。
注意next数组的匹配和KMP的匹配稍有不同。
此题的关键就在于KMP中的两个匹配函数。
#include <iostream>
#include <cstring>
#include <string>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <vector>
using namespace std;
#define INF 1000000000
#define MAXN 100005
int vis[30];
int next[MAXN];
int s[MAXN],p[MAXN];
int c1[30][MAXN],c2[30][MAXN];
int cal_s(int pos,int k)
{
if(pos<0) return 0;
int s=0;
for(int i=1;i<k;i++) s+=c1[i][pos];
return s;
}
int cal_p(int pos,int k)
{
if(pos<0) return 0;
int s=0;
for(int i=1;i<k;i++) s+=c2[i][pos];
return s;
}
bool isok(int i,int j)
{
if(!j) return true;
if (c2[p[j]][j]!=c1[s[i]][i]-c1[s[i]][i-j-1]) return false;
int cnt1=cal_s(i,s[i]);
int cnt2=cal_s(i-j-1,s[i]);
int cnt3=cal_p(j,p[j]);
return cnt1-cnt2==cnt3;
}
bool ok(int i,int j)
{
if(!j) return true;
if (c2[p[j]][j]!=c2[p[i]][i]-c2[p[i]][i-j-1]) return false;
int cnt1=cal_p(i,p[i]);
int cnt2=cal_p(i-j-1,p[i]);
int cnt3=cal_p(j,p[j]);
return cnt1-cnt2==cnt3;
}
void getnext(int n)
{
bool flag;
for(int i=1;i<n;i++)
{
int j=next[i];
flag=ok(i,j);
while(j&&!flag)
{
j=next[j];
}
if(flag)
{
next[i+1]=j+1;
}
else
{
next[i+1]=0;
}
}
}
int ans;
int kmp(int n,int m)
{
getnext(m);
bool flag;
int j=0;
for(int i=0;i<n;i++)
{
flag=isok(i,j);
while(j&&!flag) {j=next[j];flag=isok(i,j);}
if(flag) j++;
if(j==m)
{
ans++;
j=0;
}
}
return -1;
}
inline int ReadInt()
{
int flag=0;
char ch = getchar();
int data = 0;
while (ch < '0' || ch > '9')
{
if(ch=='-') flag=1;
ch = getchar();
}
do
{
data = data*10 + ch-'0';
ch = getchar();
}while (ch >= '0' && ch <= '9');
if(flag) data=-data;
return data;
}
int main()
{
int n,m,kk;
while(~scanf("%d%d%d",&n,&m,&kk))
{
ans=0;
memset(c1,0,sizeof(c1));
memset(c2,0,sizeof(c2));
for(int i=0;i<n;i++)
{
s[i]=ReadInt();
if(i){
for(int j=1;j<=kk;j++){
c1[j][i]=c1[j][i-1];}
}
c1[s[i]][i]++;
}
for(int i=0;i<m;i++)
{
p[i]=ReadInt();
if(i)
for(int j=1;j<=kk;j++)
c2[j][i]=c2[j][i-1];
c2[p[i]][i]++;
}
getnext(m);
kmp(n,m);
cout<<ans<<endl;
}
return 0;
}