1. 程式人生 > >【BZOJ3413】匹配 離線+後綴樹+樹狀數組

【BZOJ3413】匹配 離線+後綴樹+樹狀數組

script 一行 實現 wid %d dot 等於 i++ 代碼

【BZOJ3413】匹配

Description

技術分享圖片

Input

第一行包含一個整數n(≤100000)。

第二行是長度為n的由0到9組成的字符串。

第三行是一個整數m。

接下來m≤5·10行,第i行是一個由0到9組成的字符串s,保證單行字符串長度小於等於10^5,所有字符串長度和小於等於3·10^6

Output

輸出m行,第i行表示第si和S匹配所比較的次數。

Sample Input

7
1090901
4
87650
0901
109
090

Sample Output

7
10
3
4

題解:題目是問你截止到匹配之前,A串的每個位置與B串的LCP+1之和。這要利用到一個性質,兩個後綴的lcp長度等於後綴樹上兩個點的lca的mx值。所以我們對A串的反串建立後綴自動機,得到後綴樹,然後將B串放到後綴樹中進行匹配(後綴樹中匹配不同於後綴自動機中的匹配,實現比較復雜,方法也有很多,可以看代碼)。如果沒有成功匹配,則我們取最後一個成功匹配的節點,從這個點沿著到根的路徑一路走上去跑一個樹形DP即可。如果匹配成功,我們記錄一下匹配成功的節點以及匹配位置,然後離線處理。我們將原串中的點一個一個加到後綴樹中去,用樹狀數組維護DFS序得到子樹和,處理詢問時依舊跑類似於樹形DP的東西即可。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long ll;
const int N=200010;

int n,m,tot,last,cnt;
char S[N],T[N];
int ch[N][10],mx[N],pre[N],p1[N],p2[N],mn[N],pos[N],qos[N],len[N],s[N],L[N],R[N],son[N][10],siz[N];
vector<int> q[N];
vector<int>::iterator it;
int f[N];
ll ans[N];
inline int extend(int x)
{
	int p=last,np=++tot;	last=np;
	mx[np]=mx[p]+1,siz[np]=1;
	for(;p&&!ch[p][x];p=pre[p])	ch[p][x]=np;
	if(!p)	ch[1][x]=np,pre[np]=1;
	else
	{
		int q=ch[p][x];
		if(mx[q]==mx[p]+1)	pre[np]=q;
		else
		{
			int nq=++tot;	mx[nq]=mx[p]+1,R[nq]=R[q]-(mx[q]-mx[p]-1);
			pre[nq]=pre[q],pre[np]=pre[q]=nq;
			memcpy(ch[nq],ch[q],sizeof(ch[q]));
			for(;p&&ch[p][x]==q;p=pre[p])	ch[p][x]=nq;
		}
	}
	return np;
}
void dfs(int x)
{
	p1[x]=++p2[0];
	for(int i=0,y;i<10;i++)	if((y=son[x][i]))	dfs(y),siz[x]+=siz[y],mn[x]=min(mn[x],mn[y]);
	p2[x]=p2[0];
}
inline void updata(int x)
{
	for(int i=x;i<=tot;i+=i&-i)	s[i]++;
}
inline int query(int x)
{
	int ret=0,i;
	for(i=x;i;i-=i&-i)	ret+=s[i];
	return ret;
}
int main()
{
	scanf("%d%s",&n,S);
	int i,j,x,y;
	memset(mn,0x3f,sizeof(mn));
	tot=last=1;
	for(i=n-1;i>=0;i--)	pos[i]=extend(S[i]-‘0‘),mn[pos[i]]=i,R[pos[i]]=n-1;
	for(i=2;i<=tot;i++)	L[i]=R[i]-(mx[i]-mx[pre[i]])+1,son[pre[i]][S[L[i]]-‘0‘]=i;
	scanf("%d",&m);
	dfs(1);
	R[1]=-1;
	for(i=1;i<=m;i++)
	{
		scanf("%s",T),len[i]=strlen(T);
		for(x=1,j=y=0;j<len[i];j++)
		{
			if(y<=R[x])
			{
				if(S[y]==T[j])	y++;
				else	break;
			}
			else
			{
				if(son[x][T[j]-‘0‘])	x=son[x][T[j]-‘0‘],y=L[x]+1;
				else	break;
			}
		}
		qos[i]=x;
		if(j<len[i])
		{
			if(x==1)	ans[i]=n;
			else
			{
				ans[i]=n+1ll*siz[x]*min(mx[x]-mx[pre[x]],y-L[x]);
				for(x=pre[x];x!=1;x=pre[x])	ans[i]+=1ll*(mx[x]-mx[pre[x]])*siz[x];
			}
		}
		else
		{
			ans[i]=len[i]+mn[x];
			if(mn[x])	q[mn[x]-1].push_back(i);
		}
	}
	for(i=0;i<n;i++)
	{
		updata(p1[pos[i]]);
		for(it=q[i].begin();it!=q[i].end();it++)
		{
			for(j=pre[qos[*it]];j;j=pre[j])	ans[*it]+=1ll*(mx[j]-mx[pre[j]])*(query(p2[j])-query(p1[j]-1));
		}
	}
	for(i=1;i<=m;i++)	printf("%lld\n",ans[i]);
	return 0;
}

【BZOJ3413】匹配 離線+後綴樹+樹狀數組