BZOJ1461字符串的匹配(kmp)

题面

 

 

 

 1、先考虑暴力,枚举A每个子串,查看排名,与B匹配。看似时间是O(nklog(n)),但其中k是不完全的,只要不卡kmp,是可以过的。

比较排名时,若查询i之前小于等于a[i]的数,那我们需要现添加,时间上的k就是完全的;若以O(n)的复杂度,在原子串基础上向后走1位,前面删1位,保证子串个数,子串完全的情况下查询a[i]排名,那就要考虑去重的问题。

如果单纯在加入时看a[i]位上有值就不添加,此时a[i]位值虽为1,却表示2个数,删除时一次删了2个数。所以用桶存当前串中a[i]的个数

于是一个暴力AC的代码就出来了

复制代码
int lowbit(int x){return x&-x;}
int que(int x){
    int an1=0;
    while(x){an1+=c[x];x-=lowbit(x);}
    return an1;
}
void add(int x,int y){while(x<=s){c[x]+=y;x+=lowbit(x);}}
int main(){
    n=read();k=read();s=read();
    for(int i=1;i<=n;++i)  d[i]=read();
    for(int i=1;i<=k;++i)  a[i]=read(),b[i]=a[i];
    sort(b+1,b+k+1);l=unique(b+1,b+k+1)-b-1;
    for(int i=1;i<=k;++i)
        a[i]=lower_bound(b+1,b+l+1,a[i])-b;
    for(int i=1;i<=k;++i){
        if(!cnt[d[i]])  add(d[i],1);
        cnt[d[i]]++;
    }
    for(i=1;i<=k;++i)
        if(que(d[i])!=a[i])  break;
    if(i>k)  b[++ans]=1;
    for(i=2;i<=n-k+1;++i){
        if(cnt[d[i-1]]==1)  add(d[i-1],-1);
        cnt[d[i-1]]--;
        if(!cnt[d[i+k-1]])  add(d[i+k-1],1);
        cnt[d[i+k-1]]++;
        for(j=i;j<=i+k-1;++j)
            if(a[j-i+1]!=que(d[j]))  break;
        if(j>i+k-1)  b[++ans]=i;
    }cout<<ans<<endl;
    for(int i=1;i<=ans;++i)  cout<<b[i]<<endl;
}
复制代码

2、虽然上述代码过了,可这道题本是为体会kmp,所以我们再来探究kmp的思想,O(nlog(n))

仍要比较两串是否相等,但判定条件改变。可我们比较排名时要像上面,在原子串基础上向后走1位,前面删1位,保证子串个数吗?不可以的哦,一个数在不同串中排名不同,对应匹配的b也不同,nxt没有之前的性质。现在,反向操作一波:我们让nxt存储B,因为B要比较的排名是固定的,它的nxt是静态的。

去重又变为一个新问题:只需比较que(a[i])与que(a[j+1])吗?漏!考虑这样的情况A=“2 2”,B="1 3"。在查询第二个“2”时,因前已有第一个“2”,所以我们查询到排名2。这与它的排名1不符,错误地匹配到了“3”。解决方法很简单,比较小于等于的数后,再比较小于的数就可以啦(滑稽)

串与串的匹配也是个大头,怎么确定这个数在B中匹配的位置呢?在当前最长公共缀匹配不下去时,我们就删,删到第二大公共缀,还不匹配继续删,这也是kmp的重要思想。

上代码

复制代码
#include<bits/stdc++.h>
using namespace std;
int n,k,s,i,j,l,ans,a[500010],b[500010],c[10010],nxt[500010],d[500010],equ[500010],les[500010];
inline int read(){
    int x=0,f=1;char c=getchar();
    while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
    while(c<='9'&&c>='0'){x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    return x*f;
}
int lowbit(int x){return x&-x;}
int que(int x){
    int an1=0;
    while(x){an1+=c[x];x-=lowbit(x);}
    return an1;
}
void add(int x,int y){while(x<=s){c[x]+=y;x+=lowbit(x);}}
inline void get_nxt(){
    int j=0;
    for(int i=2;i<=k;++i){
        add(a[i],1);
        while(j&&(que(a[i])!=equ[j+1]||que(a[i]-1)!=les[j+1])){
            for(int kk=i-j;kk<i-nxt[j];++kk)  add(a[kk],-1);
            j=nxt[j];
        }
        j+=(que(a[i])==equ[j+1]||que(a[i]-1)==les[j+1]);
        nxt[i]=j;
    }
}
inline void match(){
    for(int i=1,j=0;i<=n;++i){
        add(d[i],1);
        while(j&&(que(d[i])!=equ[j+1]||que(d[i]-1)!=les[j+1])){
            for(int kk=i-j;kk<i-nxt[j];++kk)  add(d[kk],-1);
            j=nxt[j];
        }
        j+=(que(d[i])==equ[j+1]||que(d[i]-1)==les[j+1]);
        if(j==k)  b[++ans]=i-j+1;
    }
}
int main(){
    n=read();k=read();s=read();
    for(int i=1;i<=n;++i)  d[i]=read();
    for(int i=1;i<=k;++i){
        a[i]=read();add(a[i],1);
        equ[i]=que(a[i]);
        les[i]=que(a[i]-1);
    }memset(c,0,sizeof(c));get_nxt();
    memset(c,0,sizeof(c));match();
    cout<<ans<<endl;
    for(int i=1;i<=ans;++i)  cout<<b[i]<<endl;
}
复制代码

也可能是本人代码比较垃圾,kmp的时间比暴力还多

 

posted @   yisiwunian  阅读(244)  评论(3编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示