P4081 [USACO17DEC]Standing Out from the Herd(广义后缀自动机)

题意:

定义【独特值】表示只需要该字符串的本质不同的非空子串的个数,如 “amy” 与 “tommy” 两个串,只属于 “amy” 的本质不同的子串为 “a” “am” “amy” 共 3 个。只属于 “tommy” 的本质不同的子串为 “t” “to” “tom” “tomm” “tommy” “o” “om” “omm” “ommy” “mm” “mmy” 共 11 个。 所以 “amy” 的「独特值」为 3 ,“tommy” 的「独特值」为 11 。

题解:

对所有串建立广义SAM,然后考虑怎么计算独特值,我们想如果一个节点,如果之前没有出现过,我们就置 v i s [ r t ] = vis[rt]= vis[rt]=该串的编号,如果之前出现过了,就说明这个子串不满足独特性,直接置为-1就好了,这样就能保证都是符合条件的。

然后遍历一遍求贡献即可,每个点的贡献都是 l e n [ i ] − l e n [ f a [ i ] ] len[i] - len[fa[i]] len[i]len[fa[i]].


AC代码:

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5+50;
int nxt[MAXN][26],fa[MAXN],len[MAXN],l[MAXN],vis[MAXN],ans[MAXN];
char s[MAXN],ss[MAXN]; int ls=0,last,tot=1,n;
inline void Insert(int x){
    int p=last,np=++tot;
    last=np,len[np]=len[p]+1;
    for(;p&&!nxt[p][x];p=fa[p]) nxt[p][x]=np;
    if(!p) fa[np]=1;
    else{
        int q=nxt[p][x];
        if(len[p]+1==len[q]) fa[np]=q;
        else{
            int nq=++tot;
            len[nq]=len[p]+1;
            memcpy(nxt[nq],nxt[q],sizeof(nxt[q]));
            fa[nq]=fa[q];
            fa[q]=fa[np]=nq;
            for(;nxt[p][x]==q;p=fa[p]) nxt[p][x]=nq;
        }
    }
}
inline void update(int rt,int k){
    for(int p=rt;p && vis[p]!=k && vis[p]!=-1;p=fa[p])
        if(vis[p]!=0) vis[p]=-1;
        else vis[p]=k;
}
inline void solve(){
    int op=0;
    for(int i=1;i<=n;i++)
        for(int j=1,rt=1;j<=l[i];j++)
            rt=nxt[rt][s[++op]-'a'],update(rt,i);
    for(int i=1;i<=tot;i++)
        if(vis[i]!=-1)
            ans[vis[i]] += len[i]-len[fa[i]];
    for(int i=1;i<=n;i++) printf("%d\n",ans[i]);
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%s",ss);
        int lss=strlen(ss); last=1;
        l[i]=lss;
        for(int j=0;j<lss;j++) Insert(ss[j]-'a'),s[++ls]=ss[j];
    }
    solve();
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值