CSU2004:Finding words(含指定不相交前后缀的模式串计数)

题:http://acm.csu.edu.cn/csuoj/problemset/problem?pid=2004

题意:给定n个模式串,m个询问,每个询问是“前缀+‘*’+后缀 ”的组合的串S,输出n个模式串中有几个和S是相同的,‘*’可以是0和或更多的字符组成

4
and
abandon
append
island
3
ab*
*nd
a*nd
output
1
3
2

分析:一般是用给定的模式串来减trie图,但这题显然比较困难,解不出,那么就考虑反过来,我们拿询问的字符串进行构建;

   这里要做一下处理:以“后缀+’z+1‘+前缀”地插入trie图,z+1用来代替特殊字符;

   因为题目要求的是类似断开的询问串,而我们可以想象一下询问串的首位相连的话,同时将n个模式串首尾链接的话,前缀和后缀就连在一起了且具有唯一性,所以只要我们只要以模式串来跑trie图就行了;

   n个模式串的首尾相连根据trie的处理来,就是直接s+’z+1‘+s即可;  

   至于为啥要用’z+1‘来+在首尾相连的地方,因为如果不加的话,可能这样处理会创造出新的原本不存在的序列出来;

   注意,在跑ac自动机的时候要判断长度是否满足条件,因为我们这里相当于把n个模式串扩大了俩倍+1,而查询串的长度不变。

#include<iostream>
#include<cstdio>
#include<queue>
#include<algorithm>
#include<cstring>
using namespace std;
const int M=1e6+5;
const int N=1e5+5;
const int maxn=27;
int ans[N];
struct ac{
    int trie[M][maxn],end[M],fail[M],Len[M];
    int root,tot;
    int newnode(){
        for(int i=0;i<maxn;i++){
            trie[tot][i]=-1;
        }
        end[tot++]=0;
        return tot-1;
    }
    void init(){
        tot=0;
        root=newnode();
    }
    void insert(string buf,int id){
        int len=buf.size();
        int now=root;
        for(int i=0;i<len;i++){
            if(trie[now][buf[i]-'a']==-1)
                trie[now][buf[i]-'a']=newnode();
            now=trie[now][buf[i]-'a'];
        }
        end[now]=id;
        Len[now]=len-1;
    }
    void getfail(){
        queue<int>que;
        while(!que.empty()){
            que.pop();
        }
        fail[root]=root;
        for(int i=0;i<maxn;i++){
            if(trie[root][i]==-1)
                trie[root][i]=root;
            else{
                fail[trie[root][i]]=root;
                que.push(trie[root][i]);
            }
        }
        while(!que.empty()){
            int now=que.front();
            que.pop();
            for(int i=0;i<maxn;i++){
                if(trie[now][i]!=-1){
                    fail[trie[now][i]]=trie[fail[now]][i];
                    que.push(trie[now][i]);
                }
                else
                    trie[now][i]=trie[fail[now]][i];
            }
        }
    }
    void query(string buf){
        int len=buf.size();
        int now=root;
        int flag=(len-1)/2;
        for(int i=0;i<len;i++){
            now=trie[now][buf[i]-'a'];
            int tmp=now;
            while(tmp!=root){
                if(Len[tmp]&&end[tmp]!=0&&flag>=Len[tmp])
                    ans[end[tmp]]++;
                tmp=fail[tmp];
            }
        }
    }
}AC;
char sign='z'+1;
string s[M];
char buf[33];
int main(){

    AC.init();
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%s",buf);
        s[i]=buf;
        s[i]=s[i]+sign+s[i];
    }
    int m;
    scanf("%d",&m);
    for(int i=1;i<=m;i++){
        scanf("%s",buf);
        string s2=buf;
        if(s2=="*"){
            ans[i]=n;
            continue;
        }
        int j; 
        for(j=0;j<s2.size();j++){
            if(s2[j]=='*')
                break;
        }
        s2=s2.substr(j+1,s2.size()-j-1)+sign+s2.substr(0,j);
        AC.insert(s2,i);
    }
    AC.getfail();
    //cout<<"!!"<<endl;
    for(int i=1;i<=n;i++){
        AC.query(s[i]); 
    }
    for(int i=1;i<=m;i++)
        printf("%d\n",ans[i]);
    return 0;
}
View Code

 

posted @ 2020-02-28 00:21  starve_to_death  阅读(194)  评论(0编辑  收藏  举报