有什么用:
处理多个字符串的问题
算法实现:
#include<bits/stdc++.h>
using namespace std;
string a[300010];
int num[300010],tr[300010][26],fail[300010],ans[300010];
int tmp,n,cnt;
void insert(string s,int id){
int now=0;
for(int i=0;i<s.size();i++){
int o=s[i]-'a';
if(!tr[now][o]) tr[now][o]=++cnt;
now=tr[now][o];
}
num[now]=id;
}
void get_fail(){
int now=0;
queue<int> q;
for(int i=0;i<26;i++)
if(tr[0][i]){
q.push(tr[0][i]);
fail[tr[0][i]]=0;
}
while(!q.empty()){
int u=q.front();
q.pop();
for(int i=0;i<26;i++){
int v=tr[u][i];
if(v){
fail[v]=tr[fail[u]][i];
q.push(v);
}
else tr[u][i]=tr[fail[u]][i];
}
}
}
void query(string s){
int now=0;
for(int i=0;i<s.size();i++){
now=tr[now][s[i]-'a'];
for(int j=now;j;j=fail[j])
ans[num[j]]++;
}
}
int main(){
ios::sync_with_stdio(false);
while(cin>>n&&n){
memset(num,0,sizeof(num));
memset(ans,0,sizeof(ans));
memset(tr,0,sizeof(tr));
memset(fail,0,sizeof(fail));
cnt=0;
for(int i=1;i<=n;i++){
cin>>a[i];
insert(a[i],i);
}
get_fail();
string k;
cin>>k;
query(k);
tmp=0;
for(int i=1;i<=n;i++)
if(ans[i]>tmp) tmp=ans[i];
cout<<tmp<<"\n";
for(int i=1;i<=n;i++)
if(ans[i]==tmp) cout<<a[i]<<"\n";
}
return 0;
}
题目:
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
const int N=100007;
int ch[N][26], fail[N], val[N], cnt;
queue<int> q;
int n, top, que[N], ans[N];
char p[N], s[N];
void insert(char *s) {
int len = strlen(s), now = 0;
for (int i = 0; i < len; ++i) {
int v = s[i] - 'a';
if (!ch[now][v]) ch[now][v] = ++cnt;
now = ch[now][v];
}
val[now] = len;
}
void get_fail() {
for (int i = 0; i < 26; ++i)
if (ch[0][i]) {
q.push(ch[0][i]);
fail[ch[0][i]] = 0;
}
while (!q.empty()) {
int u = q.front();
q.pop();
for (int i = 0; i < 26; ++i)
if (ch[u][i]) {
fail[ch[u][i]] = ch[fail[u]][i];
q.push(ch[u][i]);
} else ch[u][i] = ch[fail[u]][i];
}
}
int main() {
scanf("%s", s);
scanf("%d", &n);
for (int i = 1; i <= n; ++i) {
scanf("%s", p);
insert(p);
}
get_fail();
int len = strlen(s), now = 0;
for (int i = 0; i < len; ++i) {
que[i] = now = ch[now][s[i] - 'a'];
ans[++top] = i;
if (val[now]) {
top -= val[now];
now = que[ans[top]];
}
}
for (int i = 1; i <= top; ++i)
putchar(s[ans[i]]);
putchar('\n');
return 0;
}
#include<bits/stdc++.h>
using namespace std;
const int N=33333;
int n,ch[N][2],fail[N],cnt;
bool is_word[N],vis[N],ins[N];
char s[N];
void insert(char *s){
int len=strlen(s),now=0;
for(int i=0;i<len;i++){
int x=s[i]-'0';
if(!ch[now][x]) ch[now][x]=++cnt;
now=ch[now][x];
}
is_word[now]=1;
}
void get_fail(){
queue<int> q;
for(int i=0;i<=1;i++){
if(ch[0][i]) q.push(ch[0][i]);
}
while(!q.empty()){
int now=q.front();
q.pop();
for(int i=0;i<=1;i++){
int v=ch[now][i];
if(v){
fail[v]=ch[fail[now]][i];
is_word[v]|=is_word[fail[v]];
q.push(v);
}
else ch[now][i]=ch[fail[now]][i];
}
}
}
void dfs(int now){
if(ins[now]) puts("TAK"),exit(0);
if(vis[now]||is_word[now]) return;
ins[now]=vis[now]=1;
dfs(ch[now][0]);
dfs(ch[now][1]);
ins[now]=0;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%s",s);
insert(s);
}
get_fail();
dfs(0);
puts("NIE");
return 0;
}
fail tree:
定义:按照AC自动机上fail的反向边建立的树
性质:一棵子树中所有的点都有子树的根这个后缀
应用:一个字符串在T(个体或集合)中出现了多少次
例题:
/*问题求的是x在y中出现了几次
建出fail tree后,求dfn序,记录每个节点的子树的dfn范围
每出现一个串i,在dfn[i]处+1,每撤销一个串i,在dfn[i]处-1
用树状数组,给节点x的子树求和,乃包含串x的子串个数*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int N=100010;
struct Edge{
int u,v,nxt;
}edge[N];
int head[N],cnt;
struct Query{
int x,y,ans,nxt;
}que[N];
int first[N],quetions;
int ch[N][26],fail[N],val[N],fa[N],vol[N],num,word;
int st[N],ed[N],tot;
int c[N];
char s[N];
int m;
void add_edge(int u,int v){
edge[++cnt].u=u;
edge[cnt].v=v;
edge[cnt].nxt=head[u];
head[u]=cnt;
}
void add_que(int x,int y){
que[++quetions].x=x;
que[quetions].y=y;
que[quetions].ans=0;
que[quetions].nxt=first[x];
first[x]=quetions;
}
void get_fail(){
queue<int> q;
for(int i=0;i<26;i++){
if(ch[0][i]) q.push(ch[0][i]);
}
while(!q.empty()){
int now=q.front();
q.pop();
for(int i=0;i<26;i++){
if(ch[now][i]){
fail[ch[now][i]]=ch[fail[now]][i];
q.push(ch[now][i]);
}
else ch[now][i]=ch[fail[now]][i];
}
}
}
void dfs(int u){
st[u]=++tot;
for(int i=head[u];i;i=edge[i].nxt){
int v=edge[i].v;
dfs(v);
}
ed[u]=tot;
}
void build_fail_tree(){
for(int i=1;i<=num;i++){
add_edge(fail[i],i);
}
dfs(0);
}
inline int lowbit(int x){return x&(-x);}
void add(int x,int d){
for(int i=x;i<=N-10;i+=lowbit(i)){
c[i]+=d;
}
}
int getsum(int x){
int ans=0;
for(int i=x;i>=1;i-=lowbit(i)){
ans+=c[i];
}
return ans;
}
int main(){
scanf("%s",s);
int strl=strlen(s),now=0;
for(int i=0;i<strl;i++){
if(s[i]=='P'){
val[now]=1;
vol[++word]=now;
}
else if(s[i]=='B'){
now=fa[now];
}
else{
int x=s[i]-'a';
if(!ch[now][x]){
ch[now][x]=++num;
fa[ch[now][x]]=now;
}
now=ch[now][x];
}
}
get_fail();
build_fail_tree();
scanf("%d",&m);
int x,y;
for(int i=1;i<=m;i++){
scanf("%d%d",&x,&y);
add_que(y,x);
}
now=0;word=0;
for(int i=0;i<strl;i++){
if(s[i]=='B'){
add(st[now],-1);
now=fa[now];
}
else if(s[i]=='P'){
word++;
for(int j=first[word];j;j=que[j].nxt){
int l=st[vol[que[j].y]];
int r=ed[vol[que[j].y]];
que[j].ans=getsum(r)-getsum(l-1);
}
}
else{
int x=s[i]-'a';
now=ch[now][x];
add(st[now],1);
}
}
for(int i=1;i<=m;i++){
printf("%d\n",que[i].ans);
}
return 0;
}
/*需要注意的是,这里求的是包含x的串的个数,而非x在其中出现的次数
因此在一棵子树里同一种颜色可能有两个,对这棵子树的dfs序贡献必须是1
把当前所有要打上颜色的结点按dfs序排序,每个节点上+1,排序后相邻结点的lca出-1
这样对于一棵子树里的所有颜色相同的结点,它们总的贡献是1*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
const int N=2000003;
struct Edge{
int nxt,to;
}edge[N];
int id[N],ch[N][26],fail[N],cnt2=1,head[N],cnt;
void add_edge(int u, int v){
edge[++cnt]=(Edge){head[u],v};
head[u]=cnt;
}
void insert(char *s,int num){
int x,now=1,len=strlen(s);
for(int i=0;i<len;i++){
x=s[i]-'a';
if(ch[now][x]!= 0) now=ch[now][x];
else now=ch[now][x]=++cnt2;
}
id[num]=now;
}
void get_fail(){
queue<int> q;
q.push(1);
while(!q.empty()) {
int x=q.front();
q.pop();
for (int i = 0; i < 26; ++i)
if(ch[x][i]){
int v=ch[x][i];
int f=fail[x];
while(f&&ch[f][i]==0)
f=fail[f];
fail[v]=f?ch[f][i]:1;
q.push(v);
add_edge(fail[v],v);
}
}
}
int n,tot=0,deep[N],st[N],ed[N],sz[N],top[N],son[N],fa[N];
char s[N];
void dfs(int x) {
st[x]=++tot;
sz[x]=1;
for(int i=head[x];i;i=edge[i].nxt) {
int v=edge[i].to;
fa[v]=x;
deep[v]=deep[x]+1;
dfs(v);
sz[x]+=sz[v];
if(son[x]==0||sz[v]>sz[son[x]])
son[x]=v;
}
ed[x] = tot;
}
void dfs2(int x) {
if(son[x]){
top[son[x]]=top[x];
dfs2(son[x]);
}
for(int i=head[x];i;i=edge[i].nxt){
int v=edge[i].to;
if(v!=son[x])
top[v]=v,dfs2(v);
}
}
int LCA(int x,int y){
while(top[x]!=top[y]) {
if(deep[top[x]]<deep[top[y]])
swap(x, y);
x=fa[top[x]];
}
return deep[x]<deep[y]?x:y;
}
bool cmp(int x,int y){
return st[x]<st[y];
}
int bits[N],a[N];
void update(int x, int d) {
for (;x<=tot;x+=(x&(-x)))
bits[x]+=d;
}
int sum(int x) {
int ret=0;
for (;x;x-=(x&(-x)))
ret+=bits[x];
return ret;
}
void add(char *s){
int len=strlen(s),x,now=1,tt=0;
for (int i=0;i<len;i++){
x=s[i]-'a';
if(ch[now][x]) now=ch[now][x];
else{
while(now&&ch[now][x]== 0) now=fail[now];
if(ch[now][x]) now=ch[now][x];
else now=1;
}
a[++tt]=now;
update(st[now],1);
}
sort(a+1,a+tt+1,cmp);
for (int i=2;i<=tt;i++)
update(st[LCA(a[i-1],a[i])],-1);
}
int Sum(int x) {
return sum(ed[x])-sum(st[x]-1);
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%s",s);
insert(s,i);
}
get_fail();
dfs(1);
top[1]=1;
dfs2(1);
int q,opt;
scanf("%d",&q);
while(q--){
scanf("%d",&opt);
if(opt==1) {
scanf("%s",s);
add(s);
}
else{
scanf("%d",&opt);
printf("%d\n",Sum(id[opt]));
}
}
return 0;
}
DP:
大部分f[i][j]表示当前在节点j,且串长为i时的情况
有时再加一维表示这个状态里面包含了哪些东西
题目:
//正难则反
#include<bits/stdc++.h>
using namespace std;
const int mod=10007;
const int N=100005;
int ch[N][26],fail[N],is[N];
int n,m,cnt,sum,ans,f[105][N];//f[i][j]表示当前在j点且串长为i时不经过单词结尾的路径条数
char s[N];
void insert(char *s){
int len=strlen(s),now=0;
for(int i=0;i<len;i++){
int x=s[i]-'A';
if(!ch[now][x]) ch[now][x]=++cnt;
now=ch[now][x];
}
is[now]|=1;
}
void get_fail(){
queue<int> q;
for(int i=0;i<26;i++){
if(ch[0][i]) q.push(ch[0][i]);
}
while(!q.empty()){
int now=q.front();
q.pop();
for(int i=0;i<26;i++){
if(!ch[now][i]){
ch[now][i]=ch[fail[now]][i];
continue;
}
is[ch[now][i]]|=is[ch[fail[now]][i]];
fail[ch[now][i]]=ch[fail[now]][i];
q.push(ch[now][i]);
}
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%s",s);
insert(s);
}
get_fail();
f[0][0]=1;
for(int i=1;i<=m;i++){
for(int j=0;j<=cnt;j++){
for(int k=0;k<26;k++){
if(!is[ch[j][k]])
f[i][ch[j][k]]+=f[i-1][j]%=mod;
}
}
}
for(int i=0;i<=cnt;i++)
(ans+=f[m][i])%=mod;
int sum=1;
for(int i=1;i<=m;i++)
sum=sum*26%mod;
printf("%d",(sum-ans+mod)%mod);
return 0;
}
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int N=510;
int ch[N][3],fail[N],num[N],cnt;
int n,k,dp[1005][N],vis[1005][N];
char s[N];
void insert(char *s){
int len=strlen(s),now=0;
for(int i=0;i<len;i++){
int x=s[i]-'A';
if(!ch[now][x]) ch[now][x]=++cnt;
now=ch[now][x];
}
num[now]++;
}
void get_fail(){
queue<int> q;
for(int i=0;i<3;i++){
if(ch[0][i]) q.push(ch[0][i]);
}
while(!q.empty()){
int now=q.front();
q.pop();
for(int i=0;i<3;i++){
int v=ch[now][i];
if(v) fail[v]=ch[fail[now]][i],q.push(v);
else ch[now][i]=ch[fail[now]][i];
}
}
}
int cal(int now,int val){
while(now){
val+=num[now];
now=fail[now];
}
return val;
}
int main(){
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++){
scanf("%s",s);
insert(s);
}
get_fail();
vis[0][0]=1;
for(int i=0;i<k;i++){
for(int j=0;j<=cnt;j++){
if(!vis[i][j]) continue;
for(int x=0;x<3;x++){
int now=ch[j][x];
dp[i+1][now]=max(dp[i+1][now],cal(now,dp[i][j]));
vis[i+1][now]=1;
}
}
}
int ans=0;
for(int i=0;i<=cnt;i++)
ans=max(dp[k][i],ans);
printf("%d\n",ans);
return 0;
}