题目大意:
如果是普通地判定两个串相等,我们直接做KMP就行了,而题目中判定字符串相等的条件是对应字符排名相等,所以考虑在KMP匹配时做一些改动。
考虑两个字符串前L个字符对应匹配,新加入第L+1个字符进来,只要第L+1个字符的排名也相等,那么它们对前面排名的贡献也是一样的,也就是说排名不具备后效性,只要保证当前一直相等,整个串就相等。所以在KMP时维护每个字符的排名,可以用树状数组或者线段树求和实现,每次p=fail[p]时将中间的一段清零。由于KMP的复杂度为O(n),所以这个算法的复杂度为O(nlogn).
Tips:B串可以预先处理好每个字符的排名,同时这题要注意,一个字符会出现多次,所以既要比较小于它的数个数,也要比较和它相等的数个数(对别的数的贡献)。因为这个WA了不少次。
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
const int maxn=500000+10;
int equ[maxn],les[maxn],c[maxn],a[maxn],b[maxn],n,k,s,f[maxn],tot,ans[maxn];
int lowbit(int x)
{
return x&-x;
}
void add(int x,int v)
{
while(x<=s)
{
c[x]+=v;
x+=lowbit(x);
}
}
int query(int x)
{
int res=0;
while(x)
{
res+=c[x];
x-=lowbit(x);
}
return res;
}
int main()
{
//freopen("match.in","r",stdin);
//freopen("match.out","w",stdout);
scanf("%d%d%d",&n,&k,&s);
for(int i=0;i<n;i++) scanf("%d",&a[i]);
for(int i=0;i<k;i++)
{
scanf("%d",&b[i]);add(b[i],1);
equ[i]=query(b[i]);les[i]=query(b[i]-1);
}
memset(c,0,sizeof(c));
f[0]=f[1]=0;
for(int i=1;i<k;i++)
{
int j=f[i];add(b[i],1);
while(j&&(equ[i]!=query(b[j])||les[i]!=query(b[j]-1)))
{
for(int l=i-j;l<i-f[j];l++) add(b[l],-1);
j=f[j];
}
f[i+1]= equ[j]==query(b[i])&&les[j]==query(b[i]-1) ? j+1 : 0;
}
memset(c,0,sizeof(c));
int j=0;
for(int i=0;i<n;i++)
{
add(a[i],1);
while(j&&(equ[j]!=query(a[i])||les[j]!=query(a[i]-1)))
{
for(int l=i-j;l<i-f[j];l++) add(a[l],-1);
j=f[j];
}
if(equ[j]==query(a[i])&&les[j]==query(a[i]-1))
j++;
if(j==k)
{
ans[++tot]=i-j+1;
for(int l=i-j+1;l<=i;l++) add(a[l],-1);
j=0;
}
}
printf("%d\n",tot);
for(int i=1;i<=tot;i++) printf("%d\n",ans[i]+1);
return 0;
}