4175: 小G的电话本

4175: 小G的电话本

Time Limit: 45 Sec  Memory Limit: 256 MB
Submit: 226  Solved: 52
[ Submit][ Status][ Discuss]

Description

 小G是一个商人,他有一个电话本。电话本上记下了许多联系人,如timesqr、orzyhb等等。不过Tony对其中的某个联系人的名字S特别感兴趣,他从中提取出了这个联系人的名字中的所有片段,如提取出orz的   o、r、z、or、rz、orz等等。现在他想请你统计有多少个长度为k的片段对(P[1], P[2], P[3], ..., P[k]),使得在该片段对中所有片段在S中出现次数之和为他的幸运数m?注意两个片段对不同当且仅当两个片段对的某一位的片段不同,两个片段不同当且仅当这两个片段在S中的位置不同

Input

第一行两个整数k和m,意义见题目描述;第二行给出一个字符串表示Tony喜欢的联系人名字S。

Output

输出一行一个整数ans,表示答案模1005060097。

Sample Input

3 4
aaaaa

Sample Output

6

HINT

【样例解释】



符合要求的片段对一共有6种(用[p]s表示起始位置为p的s片段):


([1]aaaa, [1]aaaaa, [1]aaaaa)、([1]aaaaa, [1]aaaa, [1]aaaaa)、


([1]aaaaa, [1]aaaaa, [1]aaaa)、([2]aaaa, [1]aaaaa, [1]aaaaa)、


([1]aaaaa, [2]aaaa, [1]aaaaa)、([1]aaaaa, [1]aaaaa, [2]aaaa)。


【数据范围】


设n表示联系人的名字的长度,联系人的名字只包含小写字母。


对于10%的数据,1 <= n <= 100, k = 1。


对于40%的数据,1 <= n <= 100, k = 2。


对于70%的数据,1 <= n <= 100000, 1 <= k <= 10。


对于100%的数据,1 <= n <= 100000, 1 <= k <= 100000, 1 <= m <= n。

Source

[ Submit][ Status][ Discuss]

对于原串,构造出它的sam,就可以统计出每种子串出现次数
设计数组f[i],为出现次数为i的串的种类数
那么f的m次方的第k位就是答案了,用NTT解决即可
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<stack>
#include<bitset>
#include<ext/pb_ds/priority_queue.hpp>
using namespace std;
 
const int maxn = (1 << 18) + 233;
typedef long long LL;
const LL g = 5;
const LL mo = 1005060097;
 
int n,m,k,N,cnt,Inv,last,rt,ch[maxn][26],len[maxn],du[maxn]
    ,fail[maxn],siz[maxn],w[maxn],_w[maxn],f[maxn],Ans[maxn];
char s[maxn]; bool vis[maxn];
 
queue <int> Q;
 
inline int Mul(const LL &x,const LL &y) {return x * y % mo;}
inline int Add(const int &x,const int &y) {return x + y < mo ? x + y : x + y - mo;}
inline int Dec(const int &x,const int &y) {return x - y >= 0 ? x - y : x - y + mo;}
 
int ksm(int x,int y)
{
    int ret = 1;
    for (; y; y >>= 1)
    {
        if (y & 1) ret = Mul(ret,x);
        x = Mul(x,x);
    }
    return ret;
}
 
void Extend(int Len,int Nex)
{
    int p = last,np = ++cnt;
    last = np; len[np] = Len; siz[np] = 1;
    while (p && !ch[p][Nex])
        ch[p][Nex] = np,p = fail[p];
    if (!p) {fail[np] = rt; return;}
     
    int q = ch[p][Nex];
    if (len[q] - len[p] == 1) fail[np] = q;
    else
    {
        int nq = ++cnt; len[nq] = len[p] + 1;
        memcpy(ch[nq],ch[q],sizeof(ch[q]));
        fail[nq] = fail[q]; fail[q] = fail[np] = nq;
        while (p && ch[p][Nex] == q)
            ch[p][Nex] = nq,p = fail[p];
    }
}
 
void Pre_Work()
{
    cin >> k >> m; rt = cnt = last = 1;
    N = 1; while (N <= m) N <<= 1; N <<= 1;
    w[0] = 1; w[1] = ksm(g,(mo - 1) / N);
    for (int i = 2; i <= N; i++) w[i] = Mul(w[i - 1],w[1]);
    for (int i = 0; i <= N; i++) _w[i] = w[N - i];
    scanf("%s",s + 1); n = strlen(s + 1);
    for (int i = 1; i <= n; i++) Extend(i,s[i] - 'a');
    for (int i = 2; i <= cnt; i++) ++du[fail[i]];
    for (int i = 2; i <= cnt; i++) if (!du[i]) Q.push(i);
    while (!Q.empty())
    {
        int k = Q.front(); Q.pop(); siz[fail[k]] += siz[k];
        --du[fail[k]]; if (!du[fail[k]]) Q.push(fail[k]);
    }
    for (int i = 2; i <= cnt; i++)
        if (siz[i] <= m) f[siz[i]] = Add(f[siz[i]],1LL * (len[i] - len[fail[i]]) * siz[i] % mo);
    Ans[0] = 1; Inv = ksm(N,mo - 2);
}
 
void Rader(int *F)
{
    int j = (N >> 1);
    for (int i = 1; i < N - 1; i++)
    {
        if (i < j) swap(F[i],F[j]); int k = (N >> 1);
        while (j >= k) j -= k,k >>= 1; j += k;
    }
}
 
void NTT(int *F,int *w,int on)
{
    Rader(F);
    for (int k = 2; k <= N; k <<= 1)
        for (int i = 0; i < N; i += k)
        {
            int now = 0;
            for (int j = i; j < i + (k >> 1); j++)
            {
                int u = F[j],v = Mul(w[now],F[j + (k >> 1)]);
                F[j] = Add(u,v); F[j + (k >> 1)] = Dec(u,v); now += N / k;
            }
        }
    if (on == -1) for (int i = 0; i < N; i++) F[i] = Mul(F[i],Inv);
}
 
void Mul_Ans()
{
    NTT(Ans,w,1); NTT(f,w,1);
    for (int i = 0; i < N; i++) Ans[i] = Mul(Ans[i],f[i]);
    NTT(Ans,_w,-1); NTT(f,_w,-1);
    for (int i = m + 1; i < N; i++) Ans[i] = 0;
}
 
void Mul_f()
{
    NTT(f,w,1); for (int i = 0; i < N; i++) f[i] = Mul(f[i],f[i]);
    NTT(f,_w,-1); for (int i = m + 1; i < N; i++) f[i] = 0;
}
 
void Ksm()
{
    for (; k; k >>= 1)
    {
        if (k & 1) Mul_Ans();
        Mul_f();
    }
}
 
int main()
{
    #ifdef DMC
        freopen("DMC.txt","r",stdin);
    #endif
     
    Pre_Work(); Ksm();
    cout << Ans[m] << endl;
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值