传送门:HDU6096
题意:给出n个字符串和q个询问,每次询问给出两个串 p 和 s 。要求统计所有字符串中前缀为 p 且后缀为 s (不可重叠)的字符串的数量。
思路:从dalao哪里学来的巧妙的解法:
首先是要离线处理。
将询问做成s + ‘#’ + p的模式串插入到AC自动机里,并记录插入的结点位置,然后将每一个原串str做成str + ‘#’ + str的主串,用AC自动机进行匹配,每匹配到一个就将对应结点的计数器++,最后对于每个询问输出其插入的结点位置的计数器就行了。
加‘#’的目的是保证匹配的是对应的后缀和前缀。
为了避免aa aa询问到aaa的情况,我们对AC自动机中的每一个结点记录一个长度LEN[i],匹配的时候只有长度大于等于LEN[i]的串才能算匹配成功。
真的是很巧妙的思路。
代码:
#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
typedef pair<int,int> P;
const int MAXN = 2000010;
int pos[MAXN];
struct Trie{
int next[MAXN][27], fail[MAXN], LEN[MAXN], ans[MAXN];
int root, L;
int insert(char buf[])
{
int len = strlen(buf), now = 0;
for(int i = 0; i < len; i++)
{
if(next[now][buf[i] - 'a'] == 0)
{
next[now][buf[i] - 'a'] = ++L;
LEN[L] = i + 1;
}
now = next[now][buf[i] - 'a'];
}
return now;
}
void init()//不要忘记初始化
{
memset(next, 0, sizeof(int) * (L + 1) * 27);
memset(fail, 0, sizeof(int) * (L + 1));
memset(LEN, 0, sizeof(int) * (L + 1));
memset(ans, 0, sizeof(int) * (L + 1));
root = L = 0;
}
void build()
{
queue<int>q;
fail[root] = root;
for(int i = 0; i < 27; i++)
if(next[root][i] == 0)
next[root][i] = root;
else
{
fail[next[root][i]] = root;
q.push(next[root][i]);
}
while(!q.empty())
{
int now = q.front(); q.pop();
for(int i = 0; i < 27; i++)
if(next[now][i] == 0)
next[now][i] = next[fail[now]][i];
else
{
fail[next[now][i]] = next[fail[now]][i];
q.push(next[now][i]);
}
}
}
void query(char buf[], int len)
{
int now = root;
for(int i = 0; buf[i]; i++)
{
now = next[now][buf[i] - 'a'];
int tmp = now;
while(tmp != root)
{
if(LEN[tmp] &l