poj3167 Cow Patterns KMP、树状数组

最近的日子被考试压得喘不过气来,说好的每天水两道题又荒废了。。。

为了让写解题报告和看解题报告不那么无聊,我决定从这篇起每篇加几句心得或感慨(很大几率是废话):

数据结构是好东西,必须信手拈来,或者准备好模块,熟练得像std::sort那样,才能克服一些稍显复杂的堆砌算法的题目。
另外,KMP是神奇的算法。

poj3167
KMP&树状数组。

这题要求第一串中间是否有与第二串的匹配的子串。只不过匹配方式改成了只要大小关系相同就算可匹配。

思考后,有个重要的结论:
当某一段s1[i..j]与s2[k..l]可以匹配,那么对于s1[i..j+1]与s2[k..l+1]是否能匹配的充要条件是:
s1[i..j]中小于s1[j+1]的数和s2[k..l]中小于s2[l+1]的数个数相同,等于的个数也相同(那么大于的个数自然也相同)。

有了这个结论,就可以通过修改KMP算法求出匹配串了。

具体就是通过树状数组统计s1[i..j],s2[k..l]中小于和等于的数的个数就可以了。这题的S范围很小,暴力也应该可以。




#include<cstdio>
#include<cstring>
using namespace std;
#define lowbit(x) (x&(-x))
#define NN 101000

int c1[NN],c2[NN],next[NN],s,ans[NN];
char s1[NN],s2[NN];

void update(int c[],int x,int val){
    while(x<=s){
        c[x]+=val;
        x+=lowbit(x);
    }
}
int query(int c[],int x){
    int ret=0;
    while(x){
        ret+=c[x];
        x-=lowbit(x);
    }
    return ret;
}

void getnext(int next[],char s2[],int l2){
    int i=0,j=-1;
    int tail=1;
    memset(c1,0,sizeof(c1));
    memset(c2,0,sizeof(c2));
    next[i]=j;
    while(i<l2){
       if ( j<0||
            (query(c2,s2[j]-1)==query(c1,s2[i]-1) && query(c2,s2[j])==query(c1,s2[i]))
          )
       {
         if (j>-1) update(c2,s2[j],1);
         if (i>0) update(c1,s2[i],1);
         i++;j++;next[i]=j;

       }
       else {
         int k=j-1;
         j=next[j];
         for(k;k>=j;k--) {if (k<0) break;update(c2,s2[k],-1);}

         int tt=i-j;
         for(tail;tail<tt;tail++) update(c1,s2[tail],-1);
       }
    }
}

int KMP(char s1[],char s2[],int l1,int l2,int next[]){
    int i,j;
    int cnt=0;
    int tail=0;
    i=j=-1;
    memset(c1,0,sizeof(c1));
    memset(c2,0,sizeof(c2));
    while(i<l1 && j<l2){
          if(j<0||
             (query(c2,s2[j]-1)==query(c1,s1[i]-1) && query(c2,s2[j])==query(c1,s1[i]))
            )
          {
            if (j>-1) update(c2,s2[j],1);
            if (i>-1) update(c1,s1[i],1);
            i++;j++;
          }
          else {
            int k=j-1;
            j=next[j];
            for(k;k>=j;k--) {if (k<0)break;update(c2,s2[k],-1);}

            int tt=i-j;
            for(tail;tail<tt;tail++) update(c1,s1[tail],-1);
          }
          if (j>=l2) {
              ans[++cnt]=i-j;
              int k=j-1;
              j=next[j];
              for(k;k>=j;k--) {if (k<0)break;update(c2,s2[k],-1);}

              int tt=i-j;
              for(tail;tail<tt;tail++) update(c1,s1[tail],-1);
          }
    }
    return cnt;
}

int main(){
    //freopen("3167in.txt","r",stdin);
    int n,kk,l1,l2,cnt,i,a;
    while(scanf("%d%d%d",&n,&kk,&s)!=EOF){
        int l1=l2=-1;
        for(i=1;i<=n;++i){
            l1++;
            scanf("%d",&a);
            s1[l1]=a;
        }
        for(i=1;i<=kk;++i){
            l2++;
            scanf("%d",&a);
            s2[l2]=a;
        }
        s1[++l1]=0;
        s2[++l2]=0;
        getnext(next,s2,l2);
        cnt=KMP(s1,s2,l1,l2,next);
        printf("%d\n",cnt);
        for(i=1;i<=cnt;++i){
            printf("%d\n",ans[i]+1);
        }

    }
    return 0;
}










  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值