HDU - 6096 :String (AC自动机,已知前后缀,匹配单词,弱数据)

Bob has a dictionary with N words in it.
Now there is a list of words in which the middle part of the word has continuous letters disappeared. The middle part does not include the first and last character.
We only know the prefix and suffix of each word, and the number of characters missing is uncertain, it could be 0. But the prefix and suffix of each word can not overlap.
For each word in the list, Bob wants to determine which word is in the dictionary by prefix and suffix.
There are probably many answers. You just have to figure out how many words may be the answer.

InputThe first line of the input gives the number of test cases T; T test cases follow.
Each test case contains two integer N and Q, The number of words in the dictionary, and the number of words in the list.
Next N line, each line has a string Wi, represents the ith word in the dictionary (0<|Wi|≤100000 0<|Wi|≤100000

)
Next Q line, each line has two string Pi , Si, represents the prefix and suffix of the ith word in the list (0<|Pi|,|Si|≤100000,0<|Pi|+|Si|≤100000 0<|Pi|,|Si|≤100000,0<|Pi|+|Si|≤100000

)
All of the above characters are lowercase letters.
The dictionary does not contain the same words.

Limits
T≤5 T≤5

0<N,Q≤100000 0<N,Q≤100000

∑Si+Pi≤500000 ∑Si+Pi≤500000

∑Wi≤500000 ∑Wi≤500000

OutputFor each test case, output Q lines, an integer per line, represents the answer to each word in the list.
Sample Input

1
4 4
aba
cde
acdefa
cdef
a a
cd ef
ac a
ce f

Sample Output

2
1
1
0

题意:已知N个单词,Q次询问,每次询问给出pre和suf,统计有多少个单词的前缀为pre,后缀为suf,而且要满足二者不相交。

思路:我们把询问建立AC自动机,单词用来跑AC自动机,跑到了就累计。

合理建立AC自动机的方式为:每个询问转为为 suf+‘{‘+pre;

跑AC自动机的方式为: 每个单词转化为 S+’{‘+S;

跑的时候如果fail可以走到某个询问,说明这个询问是这里的前后缀。(AC了但是不严谨的代码)

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int maxn=500010;
char c[maxn],s[maxn],pre[maxn],suf[maxn];
int tot,F[maxn],L[maxn],ch[maxn][27],cnt,pos[maxn];
int N,Q,dep[maxn],sum[maxn],fail[maxn],q[maxn],head,tail;
void insert(int opt){
    int Now=0,len1=strlen(suf+1),len2=strlen(pre+1);
    rep(i,1,len1){
        if(!ch[Now][suf[i]-‘a‘]) ch[Now][suf[i]-‘a‘]=++cnt,sum[cnt]=0;
        Now=ch[Now][suf[i]-‘a‘];
    }
    if(!ch[Now][26]) ch[Now][26]=++cnt,sum[cnt]=0; Now=ch[Now][26];
    rep(i,1,len2){
        if(!ch[Now][pre[i]-‘a‘]) ch[Now][pre[i]-‘a‘]=++cnt,sum[cnt]=0;
        Now=ch[Now][pre[i]-‘a‘];
    }
    pos[opt]=Now; dep[Now]=len1+len2;
}
void buildfail()
{
    head=tail=0;
    for(int i=0;i<=26;i++) if(ch[0][i]) q[++head]=ch[0][i];
    while(tail<head){
        int Now=q[++tail];
        for(int i=0;i<=26;i++){
            if(ch[Now][i]) {
                fail[ch[Now][i]]=ch[fail[Now]][i];
                q[++head]=ch[Now][i];
            }
            else ch[Now][i]=ch[fail[Now]][i];
        }
    }
}
void solve(int B,int len)
{
    int Now=0;
    rep(i,B+1,B+len) Now=ch[Now][c[i]-‘a‘];
    Now=ch[Now][26];
    rep(i,B+1,B+len){
         Now=ch[Now][c[i]-‘a‘]; int tmp=Now;
         while(dep[tmp]>len)  tmp=fail[tmp]; sum[tmp]++;
    }
}
int main()
{
    int T; scanf("%d",&T);
    while(T--){
        tot=cnt=0;
        memset(fail,0,sizeof(fail));
        memset(ch,0,sizeof(ch));
        scanf("%d%d",&N,&Q);
        rep(i,1,N){
            scanf("%s",s+1);
            L[i]=strlen(s+1); F[i]=tot;
            rep(j,1,L[i]) c[++tot]=s[j]; //保存单词
        }
        rep(i,1,Q){
            scanf("%s%s",pre+1,suf+1);
            insert(i);
        }
        buildfail();
        rep(i,1,N) solve(F[i],L[i]);
        for(int i=cnt;i>=1;i--) sum[fail[q[i]]]+=sum[q[i]]; //累加前缀和
        rep(i,1,Q) printf("%d\n",sum[pos[i]]);
    }
    return 0;
}

虽然上面的代码AC了,但是我感觉是可以hack掉,应该是数据比较水。 因为一个单词对一个询问最多有一个贡献,而这样跑下来有的单词的贡献可能大于1,所以我们加一个时间戳,保证每个单词的贡献最多为1。

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int maxn=500010;
char c[maxn],s[maxn],pre[maxn],suf[maxn];
int tot,F[maxn],L[maxn],ch[maxn][27],cnt,pos[maxn],Laxt[maxn];
int N,Q,dep[maxn],sum[maxn],fail[maxn],q[maxn],head,tail;
void insert(int opt){
    int Now=0,len1=strlen(suf+1),len2=strlen(pre+1);
    rep(i,1,len1){
        if(!ch[Now][suf[i]-‘a‘]) ch[Now][suf[i]-‘a‘]=++cnt,sum[cnt]=0;
        Now=ch[Now][suf[i]-‘a‘];
    }
    if(!ch[Now][26]) ch[Now][26]=++cnt,sum[cnt]=0; Now=ch[Now][26];
    rep(i,1,len2){
        if(!ch[Now][pre[i]-‘a‘]) ch[Now][pre[i]-‘a‘]=++cnt,sum[cnt]=0;
        Now=ch[Now][pre[i]-‘a‘];
    }
    pos[opt]=Now; dep[Now]=len1+len2;
}
void buildfail()
{
    head=tail=0;
    for(int i=0;i<=26;i++) if(ch[0][i]) q[++head]=ch[0][i];
    while(tail<head){
        int Now=q[++tail];
        for(int i=0;i<=26;i++){
            if(ch[Now][i]) {
                fail[ch[Now][i]]=ch[fail[Now]][i];
                q[++head]=ch[Now][i];
            }
            else ch[Now][i]=ch[fail[Now]][i];
        }
    }
}
void solve(int time,int B,int len)
{
    int Now=0;
    rep(i,B+1,B+len) Now=ch[Now][c[i]-‘a‘];
    Now=ch[Now][26];
    rep(i,B+1,B+len){
         Now=ch[Now][c[i]-‘a‘]; int tmp=Now;
         while(tmp) {
            if(Laxt[tmp]==time) break;
            Laxt[tmp]=time;//加一个时间戳,保证每个单词的贡献最多为1
            if(dep[tmp]<=len) sum[tmp]++;
            tmp=fail[tmp];
         }
    }
}
int main()
{
    int T; scanf("%d",&T);
    while(T--){
        tot=cnt=0;
        memset(fail,0,sizeof(fail));
        memset(ch,0,sizeof(ch));
        memset(Laxt,0,sizeof(Laxt));
        scanf("%d%d",&N,&Q);
        rep(i,1,N){
            scanf("%s",s+1);
            L[i]=strlen(s+1); F[i]=tot;
            rep(j,1,L[i]) c[++tot]=s[j]; //保存单词
        }
        rep(i,1,Q){
            scanf("%s%s",pre+1,suf+1);
            insert(i);
        }
        buildfail();
        rep(i,1,N) solve(i,F[i],L[i]);
        rep(i,1,Q) printf("%d\n",sum[pos[i]]);
    }
    return 0;
}

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

时间: 2024-08-04 09:04:35

HDU - 6096 :String (AC自动机,已知前后缀,匹配单词,弱数据)的相关文章

2017多校第6场 HDU 6096 String AC自动机

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6096 题意:给了一些模式串,然后再给出一些文本串的不想交的前后缀,问文本串在模式串的出现次数. 解法: 因为要求前缀后缀都包含的个数,所以可以把字符串a转换成a#a这样一个字符串,比如abca就转换成abca#abca 然后对于一组前缀a后缀b转换成b{a,比如ab ca,就是ca{ab, 然后对前缀后缀的串建立AC自动机,让主串去匹配,如上述例子,ca{ab满足为abca{abca的一个子串,也就

HDU 6096 String (AC自动机)

题意:给出n个字符串和q个询问,每次询问给出两个串 p 和 s .要求统计所有字符串中前缀为 p 且后缀为 s (不可重叠)的字符串的数量. 析:真是觉得没有思路啊,看了官方题解,真是好复杂. 假设原始的字符串 数组为A,首先将A中的每个字符串都进行翻转,得到字符串数组B,然后,将A和B按字典序排序. 对于一个查询来说有一个前缀p和后缀s, 所有包含前缀p的字符串在A中是连续的,可通过二分求出该区间 设为[Lp,Rp],同样,所有包含后缀s的字符串在B中也是连续的,设为[Ls,Rs] 接下来只需

HDU 6096 String(AC自动机+树状数组)

题意 给定 \(n\) 个单词,\(q\) 个询问,每个询问包含两个串 \(s_1,s_2\),询问有多少个单词以 \(s_1\) 为前缀, \(s_2\) 为后缀,前后缀不能重叠. \(1 \leq n,q \leq 10^5\) 思路 字符串题有一个小技巧,拼接字符串,中间加上连接符.如这道题,可以将查询变成 \(s_2+\text{\{}+s_1\) 的形式,相应的,把单词 \(T\) 变为 \(T+\text{\{}+T\) 的形式.那么就是普通的匹配问题了. 对于询问建立\(\text

POJ 2828 poj 2828 Buy Tickets 【树状数组,已知前n项和为K,返回n值】

题目链接:http://poj.org/problem?id=2828 在一个队列中,一个人想要插队,告诉你每个新来的人会插在i个人后面,求出最后的队列. 如果我们用模拟的话,那么时间复杂度肯定是超了:想想,如果我们逆序,那么最后来的人的位置一定是固定的,这样的话,我们将问题转化成逆序扫描给出数据,插在i个人后面这个数据就变成了在这个人前面需要留出多少个空位.如此我们只需要用树状数组记录前n项总共有多少个空位,每扫描一个数据,就找出能使得他前面正好有i个空位. 这题用树状数组或者线段树都可以,今

HDU 6096 String(AC自动机)

[题目链接] http://acm.hdu.edu.cn/showproblem.php?pid=6096 [题目大意] 给出一些字符串,给出前缀后缀模式询问,问有多少字符串符合该模式 [题解] 我们将字符串变为双倍,在中间增加拼接符, 对于每个前后缀模式,我们将其处理为[后缀+拼接符+前缀]的形式, 那么原题等价于统计前后缀模式在多少个字符串中出现, 我们用所有的前后缀模式建立AC自动机,用每个字符串在AC自动机上跑匹配, 最后统计fail链前继累加和即可. [代码] #include <cs

HDU - 6086 Rikka with String AC自动机 + dp

HDU - 6086 前缀和后缀分别建AC自动机, 考虑从两端往中间dp dp[ o ][ i ][ j ][ mask ] 表示放了前面和后面o个, 第一个自动机在 i 位置, 第二个自动机在 j 位置, 拥有的目标串的状态是mask的方案数. 对于跨过两端的东西, 我们最后处理就好了. #include<bits/stdc++.h> #define LL long long #define LD long double #define ull unsigned long long #def

HDU 2296 Ring [AC自动机 DP 打印方案]

Ring Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 3536 Accepted Submission(s): 1153 Problem Description For the hope of a forever love, Steven is planning to send a ring to Jane with a romantic

AC自动机 - AC自动机 - 多模式串的匹配运用 --- HDU 3065

病毒侵袭持续中 Problem's Link:http://acm.hdu.edu.cn/showproblem.php?pid=3065 Mean: 中文题,不解释. analyse: AC自动机的运用.这一题需要将模式串都存储下来,还有就是base的取值一定要弄清楚,由于这题的模式串都是大写字母所以我们可以通过剪枝来加速. Time complexity:o(n)+o(ml)  Source code: // Memory Time // 1347K 0MS // by : Snarl_js

HDU 2222(AC自动机模板题)

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=2222 题目大意:多个模式串.问匹配串中含有多少个模式串.注意模式串有重复,所以要累计重复结果. 解题思路: AC自动机模板题. 一开始使用LRJ的坑爹静态模板,不支持重复的模式串. 在做AC自动机+DP的时候,扒了zcwwzdjn大神的动态优化(失配指向root)写法,以及借鉴了网上的AC自动机模板, 搞出了这么一个支持重复串的模板. #include "cstdio" #include