hdu 6405 (n 个串中本质不同的子串以及他们出现在了哪些串中)

题意:给定 n 个串,询问长度为m的串在这n个串中的贡献之和的期望。贡献的求法是 出现的所有串的val的乘积。

思路:首先要求输出分数,那我们分为分子和分母来考虑,分母很好求。对于分子来说,我们只需要算出长度小于等于m的所有串的贡献之和。  问题转化为 n个串中所有本质不同的子串的贡献,那么我们统计每个状态在那些串中出现过即可。

疑问:这题我一开始采用在线的做法,就是每加入一个字母就更新当前串的所有后缀,WA掉了。

然后我改成插入n个串以后一起更新,还有每插完一个串后做一遍更新,都能AC,到底是为什么。。。

还有广义后缀自动机到底是个啥啊。。。为什么出现过的节点重新建立也不会错啊。。。

原则:广义后缀自动机不在线更新。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 300005;
LL val[maxn<<1],vis[maxn<<1];
const int ch_size = 26;
const LL mod = 1000000007;
LL mpow( LL x,int y ){
    LL res = 1;
    while(y){
        if( y&1 ) res = res*x%mod;
        y >>= 1;
        x = x*x%mod;
    }
    return res;
}
LL inv( LL x ){
    return mpow( x,mod-2 );
}
struct SAM{
    int fa[maxn<<1],    // 后缀链接
            ch[maxn<<1][ch_size],
            len[maxn<<1],   //该节点最长串的长度
            tot,    // 节点总数
            last;  // 代表当前的整个串
    void init(){
        tot = last = 0;
        fa[0] = -1;
        len[0] = 0;
        memset( ch[0],0,sizeof( ch[0] ) );
    }
    void extend( int x ){
        int p = last;
        if( ch[p][x] ){
            int q = ch[p][x];
            if( len[q] == len[p]+1 ) last = q;
            else{
                int clone = ++tot;
                len[clone] = len[p] + 1;
                last = clone;
                val[clone] = val[q];
                for (int i = 0; i < ch_size; i++) {
                    ch[clone][i] = ch[q][i];
                }
                fa[clone] = fa[q];
                fa[q]  = clone;
                while (p != -1) {
                    if (ch[p][x] == q)ch[p][x] = clone;
                    else break;
                    p = fa[p];
                }
            }
            return;
        }
        int cur = ++tot;
        memset( ch[cur],0,sizeof( ch[cur] ) );
        len[cur] = len[last]+1;
        while( p != -1 && !ch[p][x] ){
            ch[p][x] = cur;
            p  = fa[p];
        }
        if( p == -1 ){
            fa[cur] = 0;
        }else {
            int q = ch[p][x];
            if (len[q] == len[p] + 1) {
                fa[cur] = q;
            } else {
                int clone = ++tot;
                len[clone] = len[p] + 1;
                val[clone] = val[q];
                for (int i = 0; i < ch_size; i++) {
                    ch[clone][i] = ch[q][i];
                }
                fa[clone] = fa[q];
                fa[q] = fa[cur] = clone;
                while (p != -1) {
                    if (ch[p][x] == q)ch[p][x] = clone;
                    else break;
                    p = fa[p];
                }
            }
        }
        last = cur;
    }
}g;
void solve( int x,LL v,int flag ){
    while(x){
        vis[x] = flag;
        val[x] = val[x] ? val[x]*v%mod : v;
        x = g.fa[x];
        if( vis[x]==flag )break;
    }
}
LL val2[maxn<<1],vis2[maxn<<1];
char str[maxn];
vector<string> ve;
vector<int> ve2;
LL ans[1000005],mypow[1000005];
void build(){
    for( int x = 1;x <= g.tot;x++ ) {
        ans[g.len[g.fa[x]] + 1] = (ans[g.len[g.fa[x]] + 1] + val[x] + mod) % mod;
        ans[g.len[x] + 1] = (ans[g.len[x] + 1] - val[x] + mod) % mod;
    }
}
int main(){
    int n,x;
    scanf("%d",&n);
    g.init();
    for( int i = 1;i <= n;i++ ){
        scanf("%s",str);
        ve.push_back( string(str) );
    }
    for( int i = 1;i <= n;i++ ){
        scanf("%d",&x);ve2.push_back(x);
    }
    int L=0;
    for( int i = 0;i < ve.size();i++ ){
        string str = ve[i];
        int len = str.length(); L = max( L,len );
        g.last = 0;
        for( int j = 0;j < len;j++ ){
            g.extend( str[j]-'a' );
        }
    }
    for( int i = 0;i < ve.size();i++ ){
        string str = ve[i];
        int p = 0;
        for( int j = 0;j < str.size();j++ ){
            int c = str[j]-'a';
            p = g.ch[p][c];
            solve(p,ve2[i],i+1);
        }
    }
    build();
    for (int i=1;i<1000002;i++){
        ans[i] = (ans[i-1]+ans[i])%mod;
    }
    for (int i=1;i<1000002;i++){
        ans[i] = (ans[i]+ans[i-1])%mod;
    }
    mypow[0]=1;
    for (int i=1;i<1000002;i++){
        mypow[i] = 26LL*mypow[i-1]%mod;
    }
    for (int i=2;i<1000002;i++){
        mypow[i] = (mypow[i]+mypow[i-1])%mod;
    }
    for (int i=1;i<1000002;i++){
        ans[i] = 1LL*ans[i]*inv(mypow[i])%mod;
    }
    int q;
    scanf("%d",&q);
    while(q--){
        int m;scanf("%d",&m);
        printf("%lld\n", ans[m] );
    }
    return 0;
};

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值