1. 程式人生 > >【BZOJ3325】[Scoi2013]密碼 Manacher

【BZOJ3325】[Scoi2013]密碼 Manacher

一行 family main tput 小寫字母 中心 sin 字符串 space

【BZOJ3325】[Scoi2013]密碼

Description

Fish是一條生活在海裏的魚。有一天他很無聊,就到處去尋寶。他找到了位於海底深處的宮殿,但是一扇帶有密碼鎖的大門卻阻止了他的前進。通過翻閱古籍,Fish 得知了這個密碼的相關信息: 1. 該密碼的長度為N。 2. 密碼僅含小寫字母。 3. 以每一個字符為中心的最長回文串長度。 4. 以每兩個相鄰字符的間隙為中心的最長回文串長度。 很快Fish 發現可能有無數種滿足條件的密碼。經過分析,他覺得這些密碼中字典序最小的一個最有可能是答案,你能幫他找到這個密碼麽? 註意:對於兩個串A和B,如果它們的前i個字符都相同,而A的第i+1個字符比B的第i+1個字符小,那麽認為是則稱密碼A 的字典序小於密碼B 的字典序,例如字符串abc 字典序小於字符串acb。如果密碼A的字典序比其他所有滿足條件的密碼的字典序都小,則密碼A是這些密碼中字典序最小的一個。

Input

輸入由三行組成。
第一行僅含一個整數N,表示密碼的長度。
第二行包含N 個整數,表示以每個字符為中心的最長回文串長度。
第三行包含N - 1 個整數,表示每兩個相鄰字符的間隙為中心的最長回文串長度。
對於20% 的數據,1 <= n <= 100。
另有30% 的數據,1 <= n <= 1000。
最後50% 的數據,1 <= n <= 10^5。

Output

輸出僅一行。輸出滿足條件的最小字典序密碼。古籍中的信息是一定正確的,故一定存在滿足條件的密碼。

Sample Input

Sample #1
3
1 1 1
0 0
Sample #2
3
1 3 1
0 0
Sample #3
3
1 3 1
2 2

Sample Output

Sample #1
abc
Sample #2
aba
Sample #3
aaa

題解:我們模擬Manacher的過程,Manacher的時候是當str[..]=str[..]時,rl[i]++,那麽我們已知了rl,如果rl比當前值大了,則說明str[..]=str[..],否則str[..]!=str[..]。

相等的條件比較好判斷,那不等的條件呢?用f[i][j]表示i字符能不能取j,那麽如果i字符沒有與...相等的條件,就讓它等於i可以取的最小字符即可。

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int maxn=200010;
char str[maxn];
int n,mx,pos;
int rl[maxn],f[maxn][26];
inline int rd()
{
	int ret=0,f=1;	char gc=getchar();
	while(gc<‘0‘||gc>‘9‘)	{if(gc==‘-‘)f=-f;	gc=getchar();}
	while(gc>=‘0‘&&gc<=‘9‘)	ret=ret*10+gc-‘0‘,gc=getchar();
	return ret*f;
}
int main()
{
	n=rd();
	int i,j;
	for(i=1;i<=n;i++)	rl[i*2-1]=rd()+1;
	for(i=1;i<=n-1;i++)	rl[i*2]=rd()+1;
	n<<=1,str[1]=‘a‘,str[0]=‘*‘,rl[0]=1;
	for(i=0,mx=-1;i<=n;i++)
	{
		if(!(i&1))	str[i]=‘*‘;
		else
		{
			if(str[i]<‘a‘)
			{
				for(j=0;j<26;j++)	if(!f[i][j])	break;
				str[i]=‘a‘+j;
			}
		}
		if(mx>i)	j=min(mx-i+1,rl[2*pos-i]);
		else	j=1;
		for(j--;j<rl[i];j++)	str[i+j]=str[i-j];
		if(rl[i]<=i)	f[i+rl[i]][str[i-rl[i]]-‘a‘]=1;
		if(mx<i+rl[i]-1)	mx=i+rl[i]-1,pos=i;
	}
	for(i=1;i<n;i+=2)	printf("%c",str[i]);
	return 0;
}

【BZOJ3325】[Scoi2013]密碼 Manacher