1. 程式人生 > 其它 >[JZOJ3159][P5254]「JSOI2013」『字串雜湊』『列舉』廣告計劃

[JZOJ3159][P5254]「JSOI2013」『字串雜湊』『列舉』廣告計劃

題目

如今,在建築的牆面上或者籬笆樁的表面上塗上一些廣告,是一種新的吸引眼球的方法。

現在,小G 運營的一家小公司,決定也試著這樣做做廣告。小G 在他的籬笆樁上騰出了一些地方供廣告使用。每一個籬笆樁都是一個水平的11L 的4 稜柱,其中有一個1L 的面是可以做廣告的。1L 的面上劃出了L 個1*1 的小正方形(更具體地說是連續L 個水平排列的正方形),每個正方形內寫上一個字母。

​ 時間久了,廣告做多了難免會出現一些比較麻煩的情況,比如計劃改變或者製作出錯,因此小G 的倉庫裡面積累了好多沒有用的,上面已經寫上L 個字母的籬笆樁。(所有的籬笆樁的大小都是一樣的,他們唯一的區別僅僅在於上面寫了什麼字母)。

​ 小G 決定對於這些籬笆樁進行重新利用,並且有了一些新的想法。

​ 如果將這些籬笆樁豎直的疊放起來,並且依次從左往右,對於每一個籬笆樁順次從上到下讀出上面的字母,那麼我們可以得到一些新的比較長的單詞,如下圖:

​ 這些新的單詞能滿足小G 的一些新的需要。當然,基於美學考慮,小G 是不允許你刪改籬笆樁上已經寫上的字母的。

​ 我們更具體地描述這個過程。我們將K 個長度為L 的籬笆樁疊在一起,可以得到一個寫有K行L 列共K*L 個字母的面,每一個字母都在對應的唯一的格子裡。我們從左上角開始依次向下讀出每一個字母可以得到一個字母的序列,比如上圖中的這個例子,那麼我們讀出的結果就是“TOEIIZENITKN”。如果,這個串中有我們所需要的單詞,那麼顯然我們只需要將一些格子刷白,就可以得到我們所需要的了。舉個例子,比如小G 想要給聖彼得堡的足球隊澤尼特隊做個廣告,那麼很顯然只要按照上圖中的做法就可以達到小G 想要的效果了。

​ 現在小G已經想好了要做怎樣的廣告,同時也提供給你了小G倉庫中的籬笆樁的型別的描述,你可以認為每一種型別的籬笆樁都是有無數個的。現在小G 想知道至少需要多少個籬笆樁疊起來才可以做出小G 想要的廣告。

1 ≤ N , L ≤ 100 1\le N,L\le 100 1N,L100, 1 ≤ ∣ s ∣ ≤ 200 1\le |s|\le200 1s200

題解

題目大意:有 N N N種長度為 L L L的字串和一個匹配串 s s s,每種都有無數個,現選出 K K K字串,從上到下排成一個 K × L K\times L K×L的字元矩陣,從左上角開始,從上到下,從左到右順序寫下各個字元,得出一個長度為 K × L K\times L

K×L的字串 S S S,且 s s s S S S的子串,最小化 K K K和一種排列方案

考慮字串雜湊,將所有子串用27進位制壓縮存到雜湊表中(即每個字串的任意位置開頭和任意位置結尾),同時記錄每個串所在的字串的編號和開頭所在的位置。

從小到大列舉 K K K,那麼第一個合法 K K K就是答案。找出當前 K K K下匹配串 s s s被分成的 K K K個小串

如: s = abcde , K = 3 s=\text{abcde},K=3 s=abcde,K=3

3個小串分別是 ad , be , c \text{ad},\text{be},\text{c} ad,be,c

將這 K K K個小串以上述方法壓縮,在雜湊表中查詢

若第 i i i個小串在第 u u u個字串中,開頭在第 j j j列,那麼 b z [ i ] [ j ] = u bz[i][j]=u bz[i][j]=u,表示第 i i i個小串的開頭在第 j j j列可以在第 u u u個字串中找到

再列舉 s s s的第一個子串的開頭的行 i i i,列 j j j,在 b z bz bz中查詢,如果第 j j j或者 j + 1 j+1 j+1列都有對應的 u u u值,那麼就是合法答案

也就是說,在這種情況下,如果一些 b z [ u ] [ j ] bz[u][j] bz[u][j]和另一些 b z [ u ] [ j + 1 ] bz[u][j+1] bz[u][j+1]都有值( u u u表示 s s s的第 u u u個子串),那麼就合法( j j j還是 j + 1 j+1 j+1 i i i

Code

#include<cstdio>
#include<cstring>
#define md 2999999
#define hashmd 5100001 
using namespace std;
int n,l,m,id,now,num,mi[105],pos[505001][2],nxt[505001],head[5100001],hs[5100001],bj[205][205],c[205][205];
bool flag;
char s[105][105],ss[205];
void add(int x,int i,int j)
{
	pos[++num][0]=i;pos[num][1]=j;
	nxt[num]=head[x];
	head[x]=num;
}
void hash(int x,int i,int j)
{
	int p=x;
	while (hs[p])
	{
		if (hs[p]==x)
		{
			add(p,i,j);
			return;
		}
		p=(p+1)%hashmd;
	}
	hs[p]=x;
	add(p,i,j);
}
void find(int x,int r)
{
	int p=x;
	while (hs[p])
	{
		if (hs[p]==x)
		{
			for (int i=head[x];i;i=nxt[i])
				c[r][pos[i][1]]=pos[i][0],bj[r][pos[i][1]]=id;
			return;
		}
		p=(p+1)%hashmd;
	}
}
int main()
{
	mi[0]=1;
	for (int i=1;i<=100;++i)
		mi[i]=mi[i-1]*27%md;
	scanf("%d%d",&n,&l);
	for (int i=1;i<=n;++i)
	{
		scanf("%s",s[i]+1);
		for (int j=1;j<=l;++j)
		{
			int x=1;
			for (int k=j;k<=l;++k)
			{
				x=(x+(s[i][k]-'a'+1)*mi[k-j+1]%md)%md;
				hash(x,i,j);
			}
		}
	}
	scanf("%s",ss+1);
	m=strlen(ss+1);
	for (int k=1;k<=m;++k)
	{
		++id;
		for (int j=1;j<=k;++j)
		{
			int x=1,ln=0;
			now=j;
			while (now<=m)
			{
				x=(x+(ss[now]-'a'+1)*mi[++ln]%md)%md;
				now+=k;	
			}
			find(x,j);	
		} 
		for (int i=1;i<=k;++i)
		{
			int h=k-i+1;
			for (int j=1;j<=l;++j)
			{
				flag=true;
				for (int u=1;u<=k;++u)
				{
					if ((u<=h&&bj[u][j]<id)||(u>h&&bj[u][j+1]<id))
					{
						flag=false;
						break;
					}
				}
				if (flag)
				{
					printf("%d\n",k);
					for (int u=h+1;u<=k;++u) printf("%d ",c[u][j+1]);
					for (int u=1;u<=h;++u) printf("%d ",c[u][j]);
					return 0;
				}
			}
		}
	}
	printf("-1\n");
	return 0;
}

感謝LZA的部落格提供的幫助,ORZ:

https://blog.csdn.net/qq_39565901/article/details/87472261