【noi.ac】#1755. Trie

題目

題目描述
給定一個字典(字符串的集合), 有若干次詢問, 每次詢問給出一個文本串 SS, 需要回答 SS 是否有某個子串與字典內的某個單詞(字符串)同構.

一個串 SS 與一個串 TT 同構, 首先需要 |S|=|T||S|=|T|, 並且存在一個關於字符集的雙射 ff 使得 Si=f(Ti)Si=f(Ti).

比如 ABBABB 和 XYY,BAA,TSSXYY,BAA,TSS 同構, 但不和 AAB,XXY,ZZZAAB,XXY,ZZZ 同構.

輸入格式
每個輸入文件中只包含一組數據.

第一行讀入一個整數 nn, 表示字典的大小.

接下來 nn 行, 每行一個字符串, 表示字典內的單詞.

接着讀入一個整數 mm, 表示詢問的組數.

接下來 mm 行, 每行一個字符串 SS, 表示詢問的文本串.

輸出格式
輸出 mm 行, 表示對每個詢問的回答.

對於某一組詢問, 若詢問的文本串含有某個子串與字典內的單詞同構, 那麼輸出 Yes, 否則輸出 No.

樣例
輸入

3
ABB
AAB
AAAB
5
XYYX
XXXXX
WXXXY
XXY
TXTPXP
輸出

Yes
No
Yes
Yes
No
解釋

對於第一個詢問, 子串 XYY 與 ABB 同構, 子串 YYX 與 AAB 同構.

對於第三個詢問, 子串 WXX 與 ABB 同構, 子串 XXXY 與 AAAB 同構, 子串 XXY 與 AAB 同構.

對於第四個詢問, 子串 XXY 與 AAB 同構.

數據範圍
保證輸入的所有字符串的字符集爲 大寫字母.

設字典內總串長爲 NN, 設詢問串的總串長爲 MM.

(20分)N,M≤100N,M≤100.
(20分)N≤105,M≤5×105N≤105,M≤5×105, 並且每個字符串最多包含兩個不同的字符. (但所有字符都可能出現, 就是說可以出現 XXYYYXXXY 這樣的字符串, 但不會出現 ABC 這樣的串)
(20分)N≤105,M≤5×105N≤105,M≤5×105, 且字典內只有一個單詞.
(40分)N≤105,M≤5×105N≤105,M≤5×105.

思路

字符串,學一次忘一次

先考慮單個詢問怎麼做
對T建後綴數組.
令S中 mismatch 的位置是 , 使用二分維護出以S[i+1,|S|]爲前綴的後綴在後綴數組內的位置, 這一定是個區間[l,r]
枚舉 mismatch 的字符c , 用二分維護出以S[1,i-1]+c爲前綴的後綴在後綴數組內的位置, 這也是個區間[l,r]
利用這兩個區間, 利用二分可以得到在S[1,i-1]+c+S[i+1,|S|]中出現位置T.

考慮現在有模式串M , 和文本串 S, 我們想知道是否 有 S的某個子串符合M
先對模式串和文本串進行轉化, 變成一個整數序列. 對於某個字母, 如果是在串中第一次 出現, 那麼對應整數0, 如果不是, 則對應到其上一次出現的距離
在這裏插入圖片描述

多個模式串套一個AC自動機即可

代碼

#include<bits/stdc++.h>
using namespace std;
struct node
{
	map<int,node*> tr;
	node *fail,*father;
	int v,depth;
	bool end;
	node(node* fa,const int& v=0,const int& depth=0):fail(NULL),father(fa),v(v),depth(depth),end(false) { tr.clear(); }
	void getfail(node *root)
	{
		if(father==NULL) return;
		node *f=father->fail;
		while(f!=NULL)
		{
			if(v>f->depth) v=-1;
			if(f->tr.count(v)) break;
			f=f->fail;
		}
		if(f==NULL) fail=root;
		else fail=f->tr[v];
	}
};
node *root;
const int maxn=500000+5;
char s[maxn];
int yjy[maxn];
int last[128];
void pre(const int& n)
{
	node *now=root;
	for(int i=0;i<n;i++)
	{
		if(!now->tr.count(yjy[i])) now->tr[yjy[i]]=new node(now,yjy[i],i+1);
		now=now->tr[yjy[i]];
	}
	now->end=true;
}
bool dfs(const int& n)
{
	node *now=root;
	for(int i=0;i<n;i++)
	{
		int v=yjy[i];
		while(now!=NULL)
		{
			if(v>now->depth) v=-1;
			if(now->tr.count(v)) break;
			now=now->fail;
		}
		if(now==NULL) now=root;
		else now=now->tr[v];
		if(now->end) return true;
	}
	return false;
}
void bfs()
{
	queue<node*> Q;
	while(!Q.empty()) Q.pop();
	Q.push(root);
	while(!Q.empty())
	{
		node *u=Q.front();
		Q.pop();
		u->getfail(root);
		for(map<int,node*>::iterator it=u->tr.begin();it!=u->tr.end();it++)
		{
			if(it->second==NULL) continue;
			Q.push(it->second);
		}
	}
}
signed main()
{
	root=new node(NULL);
	int n,m;
	scanf("%d",&n);
	for(int i=0;i<n;i++)
	{
		scanf("%s",s);
		int n=strlen(s);
		memset(last,-1,sizeof(last));
		for(int i=0;i<n;i++)
		{
			if(last[s[i]]<0) yjy[i]=-1;
			else yjy[i]=i-last[s[i]];
			last[s[i]]=i;
		}
		pre(n);
	}
	bfs();
	scanf("%d",&m);
	for(int i=0;i<m;i++)
	{
		scanf("%s",s);
		int n=strlen(s);
		memset(last,-1,sizeof(last));
		for(int i=0;i<n;i++)
		{
			if(last[s[i]]<0) yjy[i]=-1;
			else yjy[i]=i-last[s[i]];
			last[s[i]]=i;
		}
		printf("%s\n",dfs(n)?"Yes":"No");
	}
	return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章