hdu3336 &&hdu 4552 KMP ,SAM

11 篇文章 0 订阅

      给一个串,求所有前缀在母串中出现的次数和。

      还是利用失配函数来做,构造完失配函数,然后从末尾向前枚举,对于每个点,如果没有被访问过的话,就沿其失配路径走一遍,并且每经过一个未访问过节点,迭代层数+1(初始为0),若经过的是已经访问的节点,那么迭代层数不变;同时标记该点已访问,这个点为末尾的前缀出现的次数+当前迭代的层数,然后转移到向下一个点;对KMP的失配函数深入了解一点的话,这个思路也挺好理解的,举个例子,aba....aba....aba 第一次迭代,末尾-->3 3标记掉,层数+1=1,3-->1,标记掉层数+1=2,此时a出现了两次;枚举到中间那个aba的时候,'a'-->3,层数+1=1,发现3访问过了,所以3-->1的时候层数不变还是1,然后a出现的次数+1,如果层数每次都是+1的话,显然第二次3-->1的时候,3位置的a被重复统计了,所以在从被访问过的点转移的时候,迭代的层数是保持不变的。

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <cmath>
using namespace std;
typedef long long ll;
const int maxn=210000;
bool ok[maxn];
int f[maxn];
char s[maxn],ss[maxn];
int cnt[maxn];
void getFail(char* P,int* f)
{
    int m=strlen(P);
    f[0]=0;
    f[1]=0;
    for (int i=1; i<m; i++)
    {
        int j=f[i];
        while(j && P[i]!=P[j]) j=f[j];
        f[i+1]=P[i]==P[j]?j+1:0;
    }
}
bool find(char* T,char* P,int* f)
{
    int n=strlen(T);
    int m=strlen(P);
//    getFail(P,f);
    int j=0;
    for (int i=0; i<n; i++)
    {
        while(j && P[j]!=T[i]) j=f[j];
        if (P[j]==T[i]) j++;
        if (j==m) return true;
    }
    return false;
}
int tt;
int n,m,k;

void slove(int pos,int x)
{
    if (!pos) return;
    cnt[pos]+=x;
    if (!ok[pos]) slove(f[pos],x+1);
    else slove(f[pos],x);
    ok[pos]=true;

}
int main()
{
//    freopen("in.txt","r",stdin);
    scanf("%d",&tt);
    while(tt--)
    {
        scanf("%d",&n);
        scanf("%s",s);
//        for (int i=0; i<n; i++)
//        s[i]=ss[n-i-1];
//        s[n]='\0';
        getFail(s,f);
        memset(ok,0,sizeof ok);
        memset(cnt,0,sizeof cnt);
        for (int i=n; i>=0; i--)
        {
            if (ok[i]) continue;
            int p=i;
            if (p && !ok[p])
            {
                slove(p,0);
//                cnt[f[p]]+=1;
            }
        }


        ll ans=0;
        for (int i=1; i<=n; i++)
        ans+=cnt[i]+1,ans%=10007;
        cout<<ans<<endl;
//        cout<<cnt[i]+1<<" ";
//        cout<<endl;
    }
    return 0;

}


       4552 这题好像和3336一模一样,这种子串+统计的问题几乎全能用后缀自动机(SAM)做,当时学SAM的时候顺道把这题做了,写SAM的话几乎就是个裸题了..统计一下节点出现次数,累加取模就是答案...

#include <iostream>
#include <cstdio>
#include <memory.h>
#include <algorithm>
#include <cmath>
#include <string>
#include <cstring>
using namespace std;
const int maxn=210000;
const int S=26;
const int inf=maxn;
char str[maxn>>1],s[maxn>>1],st[maxn>>1];
int c[maxn];
struct node
{
    node *par,*go[S];
    int val,sp;
}*root,*tail,que[maxn],*top[maxn];
int tot,len;
int ans;
int wtop[maxn];
int l;
inline int idx(char c)
{
    return (int)c-'a';
}
inline void add(int c,int l)
{
    node *p=tail;
    node *np=&que[tot++];
    np->val=l;
    //np->n1=0;
    while (p&&p->go[c]==NULL) p->go[c]=np,p=p->par;
    if (p==NULL) np->par=root;
    else
    {
        node *q=p->go[c];
        if (p->val+1==q->val) np->par=q;
        else
        {
            node *nq=&que[tot++];
            *nq=*q;
            nq->val=p->val+1;
            np->par=q->par=nq;
            while (p&&p->go[c]==q) p->go[c]=nq,p=p->par;
        }
    }
    tail=np;
}
inline void init()
{
    memset(que,0,sizeof que);
    len=1;
    tot=0;
    memset(wtop,0,sizeof wtop);
    ans=0;
    root=tail=&que[tot++];

}
inline void TopS()
{
    memset(c,0,sizeof c);
    for (int i=0; i<tot; i++)
    c[que[i].val]++;
    for (int i=1; i<=len; i++)
        c[i]+=c[i-1];
    for (int i=0; i<tot; i++)
    {
        top[--c[que[i].val]]=&que[i];
        wtop[c[que[i].val]]=i;
    }
}
int main()
{

    //freopen("a.txt","r",stdin);
    while (~scanf("%s",str))
    {

        l=strlen(str);

        init();
        for (int i=0; i<l; i++)
        {
            add(idx(str[i]),len++);
        }
        TopS();
        node *p=root;
        for (int i=0; i<l; i++)
        {
            p=p->go[idx(str[i])];
            p->sp=1;
        }
        for (int i=tot-1; i>0; i--)
        {
            p=top[i];
            p->par->sp+=p->sp;
            p->par->sp%=256;
        }
        p=root;
          int ans=0;
        for (int i=0; i<l; i++)
        {
            p=p->go[idx(str[i])];
            ans+=p->sp;
            ans=ans%256;
        }
        printf("%d\n",ans);
    }

    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值