CodeForces-204E:Little Elephant and Strings (后缀自动机)

The Little Elephant loves strings very much.

He has an array a from n strings, consisting of lowercase English letters. Let‘s number the elements of the array from 1 to n, then let‘s denote the element number i as ai. For each string ai (1 ≤ i ≤ n) the Little Elephant wants to find the number of pairs of integers l and r (1 ≤ l ≤ r ≤ |ai|) such that substring ai[l... r] is a substring to at least k strings from array a (including the i-th string).

Help the Little Elephant solve this problem.

If you are not familiar with the basic notation in string problems, you can find the corresponding definitions in the notes.

Input

The first line contains two space-separated integers — n and k (1 ≤ n, k ≤ 105). Next n lines contain array a. The i-th line contains a non-empty string ai, consisting of lowercase English letter. The total length of all strings ai does not exceed 105.

Output

On a single line print n space-separated integers — the i-th number is the answer for string ai.

Please, do not use the %lld specifier to read or write 64-bit integers in С++. It is preferred to use the cin, cout streams or the %I64d specifier.

Example

Input

3 1abcaab

Output

6 1 3 

Input

7 4rubikfurikababbabaaaabbbababaabababababazero

Output

1 0 9 9 21 30 0 

题意:给定N和L,输入N个字符串,对于每个字符串,输出其有多少个字串满足在这N个串里出现的次数大于等于K。

思路:广义后缀自动机模板题,或者后缀数组。

注意:注意K>N时直接输出若干个0,不然会超时,GG。

向上传递时我大胆的试了一试Bitset,结果超内存了。但是set没问题。

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=400010;
int N,K,L,times; string s[maxn];
struct SAM
{
    int np,p,nq,q,Last,cnt,cur[maxn],sum[maxn];
    int maxlen[maxn],fa[maxn],ch[maxn][26];
    SAM(){ Last=cnt=1; }
    void add(int x){
        np=++cnt; p=Last; Last=np; maxlen[np]=maxlen[p]+1;
        while(p&&!ch[p][x]) ch[p][x]=np,p=fa[p];
        if(!p) fa[np]=1;
        else{
            q=ch[p][x];
            if(maxlen[q]==maxlen[p]+1) fa[np]=q;
            else{
                nq=++cnt;
                memcpy(ch[nq],ch[q],sizeof(ch[nq]));
                cur[nq]=cur[q]; sum[nq]=sum[q];
                fa[nq]=fa[q];  fa[np]=fa[q]=nq;maxlen[nq]=maxlen[p]+1;
                while(p&&ch[p][x]==q) ch[p][x]=nq,p=fa[p];
            }
        }
        while(np){
            if(cur[np]==times||sum[np]>=K)  break;
            sum[np]++; cur[np]=times; np=fa[np];
        }
    }
    void sort()
    {

    }
    void solve(int now)
    {
        int tp=1;long long ans=0; L=s[now].length();
        for(int i=0;i<L;i++) {
            tp=ch[tp][s[now][i]-‘a‘];
            int kkk=tp;
            while(kkk>1){
               if(sum[kkk]>=K){ ans+=maxlen[kkk]; break; }
               kkk=fa[kkk];
            }
        }
        printf("%lld ",ans);
    }
}sam;
int main()
{
    scanf("%d%d",&N,&K);
    for(int i=1;i<=N;i++){
        cin>>s[i];
        L=s[i].length(); sam.Last=1; times=i;
        for(int j=0;j<L;j++) sam.add(s[i][j]-‘a‘);
    }
    if(K>N){
        for(int i=1;i<=N;i++) printf("0 ");
        return 0;
    }
    sam.sort();
    for(int i=1;i<=N;i++) sam.solve(i);
    return 0;
}

拓扑后set传递。

#include<set>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=200010;
int N,K,times; char s[maxn];
set<int>Set[maxn];
int a[maxn],b[maxn],sum[maxn],L[maxn],R[maxn];
struct SAM
{
    int np,p,nq,q,Last,cnt,maxlen[maxn],fa[maxn],ch[maxn][26];
    SAM(){ Last=cnt=1; }
    void add(int x){
        np=++cnt; p=Last; Last=np; maxlen[np]=maxlen[p]+1;
        while(p&&!ch[p][x]) ch[p][x]=np,p=fa[p];
        if(!p) fa[np]=1;
        else{
            q=ch[p][x];
            if(maxlen[q]==maxlen[p]+1) fa[np]=q;
            else{
                nq=++cnt;
                memcpy(ch[nq],ch[q],sizeof(ch[nq]));
                fa[nq]=fa[q];  fa[np]=fa[q]=nq;maxlen[nq]=maxlen[p]+1;
                while(p&&ch[p][x]==q) ch[p][x]=nq,p=fa[p];
            }
        }
    }
    void sort()
    {
        for(int i=1;i<=cnt;i++) a[maxlen[i]]++;
        for(int i=1;i<=cnt;i++) a[i]+=a[i-1];
        for(int i=1;i<=cnt;i++) b[a[maxlen[i]]--]=i;
        for(int i=1;i<=N;i++){
           int tp=1;
           for(int j=L[i];j<=R[i];j++){
              tp=ch[tp][s[j]-‘a‘];
              Set[tp].insert(i);
           }
        }
        for(int i=cnt;i>1;i--){
           int x=b[i];
           sum[x]=Set[x].size();
           int y=fa[x];
           if(Set[x].size()>Set[y].size()) swap(Set[x],Set[y]);
           for(set<int>::iterator it=Set[x].begin();it!=Set[x].end();it++)
           Set[y].insert(*it);
     }
    }
    void solve(int now)
    {
        int tp=1; long long ans=0;
        for(int i=L[now];i<=R[now];i++) {
            tp=ch[tp][s[i]-‘a‘];
            int kkk=tp;
            while(kkk>1){
               if(sum[kkk]>=K){ ans+=maxlen[kkk];break;}
               kkk=fa[kkk];
            }
        }
        printf("%lld ",ans);
    }
}sam;
int main()
{
    scanf("%d%d",&N,&K);
    for(int i=1;i<=N;i++){
        L[i]=R[i-1]+1;
        scanf("%s",s+L[i]);
        R[i]=L[i]+strlen(s+L[i])-1;
        sam.Last=1;
        for(int j=L[i];j<=R[i];j++) sam.add(s[j]-‘a‘);
    }
    if(K>N){
        for(int i=1;i<=N;i++) printf("0 ");
        return 0;
    }
    sam.sort();
    for(int i=1;i<=N;i++) sam.solve(i);
    return 0;
}

原文地址:https://www.cnblogs.com/hua-dong/p/8593182.html

时间: 2024-12-17 18:58:05

CodeForces-204E:Little Elephant and Strings (后缀自动机)的相关文章

codeforces E. Little Elephant and Strings(广义后缀自动机,Parent树)

传送门在这里. 大意: 给一堆字符串,询问每个字符串有多少子串在所有字符串中出现K次以上. 解题思路: 这种子串问题一定要见后缀自动机Parent树Dfs序统计出现次数都是套路了吧. 这道题统计子串个数,那么可以发现,若一个节点所对应的子串出现了K次,那么其贡献就是len,不需要考虑重复. 因为即使出现重复也是在两个位置. 那么只需统计以每个点结束的子串就好了. 之前的Dfs序就很套路了. 只需再跑一遍字符串,更新答案就好了. 代码: 1 #include<cstdio> 2 #include

Codechef2015 May - Chef and Strings (后缀自动机)

用后缀自动机统计出出现1~n次的串的数量f[i] 对于ans[k]=sigma(f[i]*C(i,k)) i>=k 1 const maxn=10008; 2 mo=1000000007; 3 var a,f,rig:array[0..maxn] of dword; 4 nt:array[0..maxn,'a'..'z'] of longint; 5 last,sum,i:dword; 6 s:ansistring; 7 eg:array[0..maxn*2] of record nt,v:dw

Codeforces 427D Match &amp;amp; Catch(后缀自动机)

[题目链接] http://codeforces.com/problemset/problem/427/D [题目大意] 给出一个两个字符串,求出最短且在两个字符串中唯一的公共子串. [题解] 以原字符串的两倍建立自动机,按字典序在parent树上搜索, 得到的第一个长度为n的字符串就是答案. [代码] #include <cstdio> #include <cstring> #include <algorithm> #include <vector> us

Codeforces 235C Cyclical Quest - 后缀自动机

Some days ago, WJMZBMR learned how to answer the query "how many times does a string x occur in a string s" quickly by preprocessing the string s. But now he wants to make it harder. So he wants to ask "how many consecutive substrings of s

Codeforces Round #129 (Div. 1)E. Little Elephant and Strings

Codeforces Round #129 (Div. 1)E. Little Elephant and Strings 题意:给出n个字符串,问每个字符串有多少个子串(不同位置,相同的子串视作不同)至少出现在这n个字符串中的k个当中. 解法:这题学到了一个SAM的新技能,对于这多个串,建SAM的时候,不是把它们连在一起,建立SAM,而是先给它们建立Trie树,然后广搜这棵Trie树,对于Trie树上的V节点,在建SAM的时候,它应该接在Trie树上他的父亲节点后面,我们用TtoM[U]表示Tr

E. Three strings 广义后缀自动机

http://codeforces.com/problemset/problem/452/E 多个主串的模型. 建立一个广义后缀自动机,可以dp出每个状态的endpos集合大小.同时也维护一个R[]表示那个串出现过. 所以可以算出每个状态的dp[i][k]表示第k个串在第i个状态中出现的次数. 可以知道sigma dp[i][0...k]是等于  endpos集合的大小. 然后把这个贡献加到min(i)....max(i)中去就可以了 差分一下. #include <bits/stdc++.h>

Codeforces Round #244 (Div. 2)D (后缀自动机)

Codeforces Round #244 (Div. 2)D (后缀自动机) (标号为0的节点一定是null节点,无论如何都不能拿来用,切记切记,以后不能再错了) 这题用后缀自动机的话,对后缀自动机的很多性质有足够深刻的理解.没想过后缀数组怎么做,因为不高兴敲.... 题意:给出两个长度均不超过5000的字符串s1,s2,求这两个串中,都只出现一次的最短公共子串. 解题思路:求的是公共子串,然后对出现的次数又有限制,第一想法就是后缀自动机啊,后缀自动机处理子串出现次数再合适不过了.做法是这样的

Match &amp; Catch CodeForces - 427D 后缀自动机水题

题意: 给出两个字符串a,b,求一个字符串,这个字符串是a和b的子串, 且只在a,b中出现一次,要求输出这个字符串的最小长度. 题解: 将a串放入后缀自动机中,然后记录一下每个节点对应的子串出现的次数 然后把b串取自动机中匹配 然后判断一下 1 #include <set> 2 #include <map> 3 #include <stack> 4 #include <queue> 5 #include <cmath> 6 #include &l

poj2774 Long Long Message(后缀数组or后缀自动机)

转载请注明出处: http://www.cnblogs.com/fraud/          ——by fraud Long Long Message Time Limit: 4000MS   Memory Limit: 131072K Case Time Limit: 1000MS Description The little cat is majoring in physics in the capital of Byterland. A piece of sad news comes t