1. 程式人生 > 實用技巧 >UVA 11107 Life Forms

UVA 11107 Life Forms

https://vjudge.net/problem/UVA-11107

題目

給n個字串,每個字串只由小寫字母構成,求所有長度最長、在大於n/2個字串中出現的字串。如果有多個,按照字典序輸出。

1<=n<=100,每個字串不超過1000個字母。

時限6666ms

題解

不會做,書上提示用比較大的字元把這些字串連線起來,然後求字尾陣列和height陣列,然後二分最大長度,將height>二分長度份的段,O(n)判斷一下有多少種字串,然後得到答案,時間複雜度O(nlogn)

然後問如何使用棧來做,然後我就按照棧來做了,寫了很久,發現又慢又難寫……大部分都是60ms AC,我交上去是70ms

可能是用了bitset才特別慢,但是我不想改了……

類似於單調棧,當遇到小於前一個height的時候,彈棧並計算字串出現次數。由於出現的字串編號是亂的,需要使用長度為100的bitset來統計

對height=0的字尾,只管彈棧,把這個編號放進下一個字尾,甚至可以所有後綴的編號都放進下一個字尾

然後就是慢慢調了

AC程式碼

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<bitset>
using namespace std;
#define MAXN 107
unsigned char s[MAXN*1000], id[MAXN*1000];
int sa[MAXN*1000], t1[MAXN*1000], t2[MAXN*1000], c[MAXN*1000], l, m;
int rk[MAXN*1000], height[MAXN*1000], hid[MAXN*1000];
inline void calcsa() {
	int *x=t1, *y=t2;
	int now=0;
	for(int i=0; i<m; i++) c[i]=0;
	for(int i=0; i<l; i++) c[x[i]=s[i]-'a']++;
	for(int i=1; i<m; i++) c[i]+=c[i-1];
	for(int i=l-1; i>=0; i--) sa[--c[x[i]]]=i;
	for(int k=1; k<=l; k<<=1) {
		int p=0;
		for(int i=l-k; i<l; i++) y[p++]=i;
		for(int i=0; i<l; i++) if(sa[i]>=k) y[p++]=sa[i]-k;

		for(int i=0; i<m; i++) c[i]=0;
		for(int i=0; i<l; i++) c[x[y[i]]]++;
		for(int i=1; i<m; i++) c[i]+=c[i-1];

		for(int i=l-1; i>=0; i--) sa[--c[x[y[i]]]]=y[i];
		swap(x,y);
		p=1; x[sa[0]]=0;
		for(int i=1; i<l; i++) {
			x[sa[i]]=y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+k]==y[sa[i]+k]?p-1:p++;
		}
		if(p>=l) break;
		m=p;
	}
	for(int i=0; i<l; i++) rk[i]=x[i];
	for(int i=0; i<l; i++) {
		if(s[i]>'z') now=s[i]-'z';
		id[i]=now;
	}
	for(int i=0, k=0; i<l; i++) if(rk[i]) {
		if(k) k--;
		int j=sa[rk[i]-1];
		while(i+k<l && j+k<l && s[i+k]==s[j+k]) k++;
		height[rk[i]]=k;
	}
	for(int i=0; i<l; i++) {
		hid[i]=id[sa[i]];
	}
}
inline void putsn(char *s, int n) {
	while(0<n--) putchar((*(s++)));
	putchar('\n');
}
int sth[MAXN*1000], sz, stk[MAXN*1000];
bitset<100> stb[MAXN*1000], now;

int n, ch=0, flg[MAXN*1000];
inline void solve_stack() {

	sz=0; //sth[0]=l+7; stb[0].reset(); stb[0].set(hid[0]);
	int ans=-1; bool ok=false;
	height[l]=0;
	for(int i=0; i<=l; i++) {
//		printf("*%d %d %d=%s", sa[i], hid[i], height[i], s+sa[i]);
		int ht=0x3f3f3f3f;
		now.reset();
		if(sz>0) {
			int kk=stk[sz-1];
			while(sz>0 && sth[sz-1]>height[i]) {
				if(ht<0x3f3f3f3f && ht>height[i]) {
					now.set(hid[stk[sz-1]]);
					if(now.count()>n/2) break;
				}
				now|=stb[sz-1];

				ht=min(ht,sth[--sz]);
			}
			while(sz>0 && sth[sz-1]>height[i]) sz--;
			int nans=now.count();
			if(nans>n/2) {
				ok=true;
				nans=ht;
				if(nans>ans) {
					ans=nans, flg[kk]=++ch;
				} else if(nans==ans) {
					flg[kk]=ch;
				}
			}
		}
//		printf("\t%d\n", now.count());
		if(height[i]>0) {
			sth[sz]=height[i];
			now.set(hid[i-1]); now.set(hid[i]);
			stb[sz]=now;
			stk[sz]=i;
			sz++;
		}
	}
	if(ok) {
		for(int i=1; i<l; i++) if(flg[i]==ch) {
			putsn((char*)s+sa[i], ans);
		}
	} else puts("?");
}
int main() {
	bool fi=false;
	memset(flg,-1,sizeof flg);
	while(~scanf("%d", &n) && n) {
		if(fi) putchar('\n'); else fi=true;
		char *p=(char*) s;
		m=26+n;
		for(int i=0; i<n; i++) {
			scanf("%s", p);
			int l=strlen(p);
			p+=l;
			*p=(unsigned)'z'+1+i;
			p++;
		}
		if(n==1) {*(--p)=0; puts((char*)s);continue;}
		*p=0;
		l=strlen((char*) s);
		calcsa();
		solve_stack();
	}
}