多校6 String(ac自动机)

题意:

给一本有\(n\)个单词的字典

\(q\)个查询 \(pref_i,suff_i\) 查询字典里有多少单词前缀匹配\(pref_i\),后缀同时匹配\(suff_i\),并且

\(pref_i\)和\(suf_i\)不相交

\(0 < n ,q <= 1e5\)

$ \sum (|pref_i| + |suff_i|) <= 5e5$

$ \sum |w_i| <= 5e5$

保证每组查询的前后缀不相交

思路:

forever97大神的这个思路很不错,比起题解的做法来说,更加符合字符串的套路吧

所有查询一起处理,把查询按$suff_i $ * $pref_i $,

中间用*隔开的形式拼接起来,丢到ac自动机里

然后对于字典里的每个单词 扩展成两倍,同样中间用 * 隔开,

在ac自动机里查询有多少个前后缀是该字符串的子串,比较一下长度就可以知道前后缀是否相交

这样就变成了最简单的在一个文本串中找哪些字符串出现过的问题了

#include<bits/stdc++.h>
#define LL long long
#define P pair<int,int>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define ls rt<<1
#define rs (rt<<1|1)
using namespace std;
int read(){
int x = 0;
char c = getchar();
while(c < '0' || c > '9') c = getchar();
while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
return x;
}
const int SIZE = 27;
const int MAXNODE = 1e6 + 10;
const int N = 1e6 + 10;
char word[N],wo[N];
char pre[N],suf[N];
int w_len[N];
int pos[N];
int ans[N];
struct AC{
int ch[MAXNODE][SIZE];
int f[MAXNODE],last[MAXNODE],val[MAXNODE],length[MAXNODE];
int sz;
void init(){sz = 1;memset(ch[0],0,sizeof(ch[0]));length[0]=0;}
int idx(char c){return c - 'a';}
int _insert(char *s,int v){
int u = 0,len = strlen(s);
for(int i = 0;i < len;i++){
int c = idx(s[i]);
if(!ch[u][c]){
memset(ch[sz],0,sizeof ch[sz]);
val[sz] = 0;
length[sz] = i + 1;
ch[u][c] = sz++;
}
u = ch[u][c];
}
if(!val[u]) {ans[u] = 0,val[u] = v;}
return u;
}
void getFail(){
queue<int> q;
f[0] = 0;
for(int c = 0;c < SIZE;c++){
int u = ch[0][c];
if(u){
f[u] = 0;
q.push(u);
last[u] = 0;
}
}
while(!q.empty()){
int r = q.front();q.pop();
for(int c = 0;c < SIZE;c++){
int u = ch[r][c];
if(!u){ch[r][c] = ch[f[r]][c];continue;}
q.push(u);
int v = f[r];
while(v && !ch[v][c]) v = f[v];
f[u] = ch[v][c];
last[u] = val[f[u]]?f[u]:last[f[u]];
}
}
}
void cal(int len,int u){
if(u) {
if(length[u] <= len) ans[u]++;
cal(len,last[u]);
}
}
void Find(char *s,int l){
int len = strlen(s),u = 0;
for(int i = 0;i < len;i++){
u = ch[u][idx(s[i])];
if(val[u]) cal(l,u);
else if(last[u]) cal(l,last[u]);
}
}
}ac;
int main(){ int T,n,q;
T = read();
while(T--){
n = read(),q = read();
int total = 0;
for(int i = 1;i <= n;i++){
scanf("%s",wo);
w_len[i] = strlen(wo);
for(int j = 0;j < w_len[i];j++) word[j + total] = wo[j];
total += w_len[i];
}
ac.init();
for(int i = 1;i <= q;i++){
scanf("%s%s",pre,suf);
int l = strlen(pre),r = strlen(suf);
suf[r]='z'+1;
for(int j = 0;j < l;j++) suf[r+1+j] = pre[j];
suf[l + r + 1] = '\0';
pos[i] = ac._insert(suf,i);
};
ac.getFail();
int now = 0;
for(int i = 1;i <= n;i++) {
for(int j = 0;j < w_len[i];j++) pre[j + w_len[i] + 1] = pre[j] = word[now + j];
pre[w_len[i]] = 'z' + 1;
pre[2 * w_len[i] + 1] = '\0';
ac.Find(pre,w_len[i]+1);
now += w_len[i];
}
for(int i = 1;i <= q;i++) printf("%d\n",ans[pos[i]]);
}
return 0;
}

最新文章

  1. A cost-effective recommender system for taxi drivers
  2. 获取外部配置JDBC文件 写给自己
  3. 函数return/获取元素样式/封装自己的库
  4. POJ 2195 D - Going Home 费用流
  5. 数据库连接池c3p0和dbcp
  6. MKServerBuilder.psd1
  7. QtNetwork说明(两)使用QT实现360的ctrl+ctrl特征
  8. REST认识
  9. 今天学习了下,如何破解wifi
  10. [Luogu 2816]宋荣子搭积木
  11. 探索SQL Server元数据(一)
  12. [Python学习笔记] turtle库的基本使用
  13. python 0,1行列问题
  14. BigDecimal用法总结
  15. SQL Server 2008&mdash;&mdash;SQL命令INSERT
  16. MySQL查看某库表大小及锁表情况
  17. Locust性能测试4-参数关联
  18. Asp.Net Core MVC框架内置过滤器
  19. Linux命令详解-ftp服务器配置
  20. pfring破解DNA限制

热门文章

  1. centos7 多网卡修改默认路由
  2. linux学习笔记二:三种网络配置
  3. mysql,oracle表数据相互导入
  4. 汇编:1位16进制数到ASCII码转换
  5. 简单了解,使用oracle中的索引,表分区
  6. libvirt笔记(未完待续)
  7. spring源码学习中的知识点
  8. Java语言基础---转义符
  9. android获取未安装APK签名信息及MD5指纹
  10. Android 人脸识别