1. 程式人生 > 其它 >【Coel.解題報告】[TJOI2013]單詞

【Coel.解題報告】[TJOI2013]單詞

題前碎語

這篇報告和上一篇是同一天寫的,所以沒什麼好說的。
直接進入正題。


題目梗概:[TJOI2013]單詞

洛谷傳送門

題目描述

小張最近在忙畢設,所以一直在讀論文。一篇論文是由許多單片語成但小張發現一個單詞會在論文中出現很多次,他想知道每個單詞分別在論文中出現了多少次。

輸入格式

第一行一個整數 \(N\),表示有 \(N\) 個單詞。

接下來 \(N\) 行每行一個單詞,每個單詞都由小寫字母 \(a-z\) 組成。

輸出格式

輸出 \(N\) 個整數,第 \(i\) 行的數表示第 \(i\) 個單詞在文章中出現了多少次。


看起來是多模式串匹配,想當然地開一個\(AC\)自動機
但是這題有相同單詞,所以直接建\(Trie\)

樹會把相同的樹覆蓋,答案就錯了。
可以利用\(Fail\)陣列的特性,給每個節點加一個權值表示相同字串數量。
這樣,在計算的時候只需要把每個相對應的數量相加即可。
程式碼如下:

#include<cstdio>
#include<cstring>
#include<queue>
#define maxn 1000050
#define maxc 26
using namespace std;
int n,cnt=1,tr[maxn][maxc],fail[maxn],vis[maxn],ans[maxn],ord[maxn];
char s[maxn];
queue<int>Q;
inline void insert(char *s,int x)
{
	int u=0,len=strlen(s);
	for(register int i=0;i<len;i++)
	{
		char c=s[i]-'a';
		if(!tr[u][c])
			tr[u][c]=cnt++;
		u=tr[u][c];
		ans[u]++;//節點權值
	}
	vis[x]=u;//第i個數組對應的節點
}
inline void getfail()
{
	cnt=0;//把建樹時的cnt做一個重複利用
	for(register int i=0;i<maxc;i++)
		if(tr[0][i])Q.push(tr[0][i]);
	while(!Q.empty())
	{
		int u=Q.front();
		Q.pop();
		ord[++cnt]=u;
		for(register int i=0;i<maxc;i++)
		{
			if(tr[u][i])
			{
				fail[tr[u][i]]=tr[fail[u]][i];
				Q.push(tr[u][i]);
			}
			else tr[u][i]=tr[fail[u]][i];
		}
	}
}
inline void query()
{
	for(register int i=cnt;i;i--)
		ans[fail[ord[i]]]+=ans[ord[i]];//非常簡短的query
}
inline void write(int x)//考慮到輸出量會非常大,寫一個快寫
{
	if(x>9)write(x/10);
	putchar(x%10+'0');
}
int main()
{
	scanf("%d",&n);
	for(register int i=1;i<=n;i++)
	{
		scanf("%s",s);
		insert(s,i);
	}
	getfail();
	query();
	for(register int i=1;i<=n;i++)
		write(ans[vis[i]]),puts("");//注意快寫不能輸出換行,用puts來補充
}

題後閒話

機房20:05要強制關機,有點難受。
今天就寫到這裡吧。