【AC自动机 父串中有最大数量的子串个数】 洛谷 P3796 模板题

https://www.luogu.org/problemnew/show/P3796

题目描述

有N个由小写字母组成的模式串以及一个文本串T。每个模式串可能会在文本串中出现多次。你需要找出哪些模式串在文本串T中出现的次数最多。

输入输出格式

输入格式:

输入含多组数据。

每组数据的第一行为一个正整数N,表示共有N个模式串,1≤N≤150。

接下去N行,每行一个长度小于等于70的模式串。下一行是一个长度小于等于10^6的文本串T。

输入结束标志为N=0。

输出格式:

对于每组数据,第一行输出模式串最多出现的次数,接下去若干行每行输出一个出现次数最多的模式串,按输入顺序排列。

输入输出样例

输入样例#1: 复制

2
aba
bab
ababababac
6
beta
alpha
haha
delta
dede
tata
dedeltalphahahahototatalpha
0

输出样例#1: 复制

4
aba
2
alpha
haha
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn =  2*1e5+9;

int trie[maxn][26]; //字典树
int cntword[maxn];  //记录该单词出现次数
int fail[maxn];     //失败时的回溯指针
int ans[maxn];
int cnt = 0,temp=0;
int n;
string s[maxn*10];

void insertWords(string s,int v){
    int root = 0;
    for(int i=0;i<s.size();i++){
        int next = s[i] - 'a';
        if(!trie[root][next])
            trie[root][next] = ++cnt;
        root = trie[root][next];
    }
    cntword[root]=v;      //当前节点单词数+1
}
void getFail(){
    queue <int>q;
    for(int i=0;i<26;i++){      //将第二层所有出现了的字母扔进队列
        if(trie[0][i]){
            fail[trie[0][i]] = 0;
            q.push(trie[0][i]);
        }
    }

//fail[now]    ->当前节点now的失败指针指向的地方
////tire[now][i] -> 下一个字母为i+'a'的节点的下标为tire[now][i]
    while(!q.empty()){
        int now = q.front();
        q.pop();

        for(int i=0;i<26;i++){      //查询26个字母
            if(trie[now][i]){
                //如果有这个子节点为字母i+'a',则
//让这个节点的失败指针指向(((他父亲节点)的失败指针所指向的那个节点)的下一个节点)
                //有点绕,为了方便理解特意加了括号

                fail[trie[now][i]] = trie[fail[now]][i];
                q.push(trie[now][i]);
            }
            else//否则就让当前节点的这个子节点
                //指向当前节点fail指针的这个子节点
                trie[now][i] = trie[fail[now]][i];
        }
    }
}


void query(string s){
    int now=0;
    for(int i=0;i<s.size();i++){
        now=trie[now][s[i]-'a'];
        for(int j=now;j;j=fail[j])ans[cntword[j]]++;
    }
}

int main() {
    while(cin >> n && n)
    {
        memset(cntword,0,sizeof(cntword));
        memset(ans,0,sizeof(ans));
        memset(trie,0,sizeof(trie));
        memset(fail,0,sizeof(fail));
        cnt=0;
        for(int i=1;i<=n;i++){
            cin >> s[i] ;
            insertWords(s[i],i);
        }
        getFail();
        string s1;
        cin>>s1;
        query(s1);
        temp=0;
        for(int i=1;i<=n;i++)
            if(ans[i]>temp)
                temp=ans[i];
        cout<<temp<<"\n";

        for(int i=1;i<=n;i++)
            if(ans[i]==temp)
                cout<<s[i]<<"\n";

    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_41037114/article/details/89378258