UVa 1227 The longest constant gene
問題
A,C,G,Tからなる文字列n個が与えられる。
n個全てに出現する文字列のうち、最も長さの長い文字列の長さを求めよ。
制約条件
n≦6
それぞれの文字列の長さは100万以下
方針
S1 + "$1" + S2 + "$2" + ... + "$n"という文字列の
suffix arrayを作る。
(ただし$iは適当な区切り文字)
suffix arrayからLCPを求めて、
LCPの配列を尺取する。
suffix arrayの値から、そのsuffixが何番目の文字列由来かがわかる。
全ての文字列由来の文字が含まれている区間について、
その区間上のLCPの最小値の最大値が求める答えになる。
これは蟻本にあった本の問題のような尺取をしつつ、
最小値はdeqを使って増加するところだけを覚えておくようにすればO(len)で出来る。
TLEが厳しく、suffix arrayの構築にO(len log len)をかけるとTLE.
O(n)のsuffix arrayの構築はhttp://www.cs.sysu.edu.cn/nong/index.files/Two%20Efficient%20Algorithms%20for%20Linear%20Suffix%20Array%20Construction.pdfにコードが載ってた。
ソースコード
const int MX = 6000100; char in[MX]; int n, m, len[MX], deq[MX]; int which[MX], sa[MX], lcp[MX], b[MX]; void buildLCP(char *t, int n, int *a, int *lcp) { int h = 0; rep(i, n+1) b[a[i]] = i; rep(i, n+1) { if (b[i]){ for (int j = a[b[i]-1]; j+h<n && i+h<n && t[j+h] == t[i+h]; ++h); lcp[b[i]] = h; } else lcp[b[i]] = -1; if (h > 0) --h; } } int main(){ int cs; scanf("%d", &cs); while(cs--){ scanf("%d", &n); m = 0; rep(i,n){ scanf("%s", in+m); len[i] = strlen(in+m); m += len[i]; in[m++] = '0' + i; len[i] = m; } len[m] = 0; SA_IS((unsigned char*)in, sa, m+1, 256, sizeof(char)); buildLCP(in, m, sa, lcp); int cur = 0; rep(i,m+1){ if(i == len[cur] - 1) which[b[i]] = n, cur++; else which[b[i]] = cur; } int ans = 0, k = 0, use[6] = {}, h = 0, t = 0; for(int l = 0, r = 0; l < m; l++){ while(l > r) r++; while(r <= m && k < n){ int idx = which[r]; if(idx < n && use[idx]++ == 0) k++; while(h < t && lcp[deq[t-1]] >= lcp[r]) t--; if(r) deq[t++] = r; r++; } if(k < n) break; ans = max(ans, lcp[deq[h]]); if(h < t && deq[h] <= l+1) h++; int idx = which[l]; if(idx < n && --use[idx] == 0) k--; } printf("%d\n", ans); } return 0; }