LOJ#2278. 「HAOI2017」字符串【差異區間長度限制下的字符串匹配】

題目描述:

link

在這裏插入圖片描述
在這裏插入圖片描述

題目分析:

相當於可以將 pip_i 中的一段長度爲 kk 的區間變爲通配符然後進行匹配。

那麼 S[l...r]S[l...r] 能和 pi[1...L]p_i[1...L] 匹配的條件是存在一個 pip_i 的前綴 pi[1...j]p_i[1...j],使得 S[l...l+j1]S[l...l+j-1]pi[1...j]p_i[1...j] 匹配,S[l+j+k...r]S[l+j+k...r]pi[j+k+1...L]p_i[j+k+1...L] 匹配。

考慮枚舉 pip_i 的一個前綴 pi[1...j]p_i[1...j],找出它在 SS 中的匹配位置,因爲是多模式串匹配,我們用AC自動機來做。相當於 S[1...x]S[1...x] 對應的節點在 pi[1...j]p_i[1...j] 的子樹中。

對於找到的匹配位置 xx,還要滿足 p[j+k+1,L]p[j+k+1,L]S[x+k+1...n]S[x+k+1...n] 的後綴,這相當於在反串的AC自動機中 S[x+k+1...n]S[x+k+1...n] 對應的節點在 p[j+k+1,L]p[j+k+1,L] 的子樹中。

這相當於是個二維數點問題,但是可以離線查詢,在第一棵樹 dfs 進入和出來時的答案作差就可以得到子樹內在第二棵樹的貢獻。第二棵樹用樹狀數組在dfs序上單點修改區間查詢即可。

可能 pip_i 在枚舉多個前綴的時候會在同一個位置被匹配多次:
在這裏插入圖片描述
對應的 kk 的區間一定是連續的,兩個相鄰的選 kk 方案重複相當於 pi[1...j+1],p1[j+k+1...L]p_i[1...j+1],p_1[j+k+1...L] 分別與 S[l...l+j],S[l+j+k...n]S[l...l+j],S[l+j+k...n] 匹配(即一個長度爲 k1k-1 的匹配),去掉即可。

Code:

#include<bits/stdc++.h>
#define maxn 400005
#define maxc 94
using namespace std;
int n,k,m,ans[maxn],pos[maxn];
char s[maxn],t[maxn];
int ch[maxn][maxc],fail[maxn],tot;
struct node{int id,x,y;};
vector<node>Q[maxn];
vector<int>S[maxn],G[maxn];
int in[maxn],out[maxn],tim,a1[maxn],a2[maxn];
void dfs1(int u){in[u]=++tim; for(int v:G[u]) dfs1(v); out[u]=tim;}
void upd(int *a,int i){for(;i<=tim;i+=i&-i) a[i]++;}
int qsum(int *a,int i){int s=0;for(;i>0;i-=i&-i) s+=a[i];return s;}
void dfs2(int u){
	for(node q:Q[u]) ans[q.id]-=qsum(a1,out[q.x])-qsum(a1,in[q.x]-1)-(qsum(a2,out[q.y])-qsum(a2,in[q.y]-1));
	for(int x:S[u]) upd(a1,in[pos[x+k+1]]),upd(a2,in[pos[x+k]]);
	for(int v:G[u]) dfs2(v);
	for(node q:Q[u]) ans[q.id]+=qsum(a1,out[q.x])-qsum(a1,in[q.x]-1)-(qsum(a2,out[q.y])-qsum(a2,in[q.y]-1));
}
int main()
{
	scanf("%d%s%d",&k,s+1,&n),m=strlen(s+1);
	for(int o=1;o<=n;o++){
		scanf("%s",t+1); int L=strlen(t+1);
		if(L<=k) {ans[o]=m-L+1; continue;}
		int r=0,c;
		for(int i=1;i<=L;i++,r=ch[r][c])
			if(!ch[r][c=t[i]-33]) ch[r][c]=++tot;
		r=pos[L+1]=0;
		for(int i=L;i>=1;pos[i]=r=ch[r][c],i--)
			if(!ch[r][c=t[i]-33]) ch[r][c]=++tot;
		r=0;
		for(int i=0;i+k<=L;i++,r=ch[r][t[i]-33])
			Q[r].push_back((node){o,pos[i+k+1],i?pos[i+k]:maxn-1});
	}
	static int q[maxn],L,R; q[L=R=1]=0;
	while(L<=R){
		int r=q[L++],c; if(r) G[fail[r]].push_back(r);
		for(int i=0;i<maxc;i++)
			if(c=ch[r][i]) fail[c]=r?ch[fail[r]][i]:0,q[++R]=c;
			else ch[r][i]=ch[fail[r]][i];
	}
	dfs1(0); pos[m+1]=0;
	for(int i=m,r=0;i>=1;i--) pos[i]=r=ch[r][s[i]-33];
	for(int i=0,r=0;i+k<=m;i++,r=ch[r][s[i]-33]) S[r].push_back(i);
	dfs2(0);
	for(int i=1;i<=n;i++) printf("%d\n",ans[i]);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章