立命館合宿2012 day3 問題F Icy Composer (AOJ 2367)
問題
日本語なので本文参照(http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=2367)
2(3(b)4(ab)x)
のように圧縮された文字列sが与えられる。
tの空でない全ての連続する部分文字列のうち、展開後のsに含まれるものはいくつあるか
を求める問題。
制約条件
n≦250
m≦400
k≦5
方針
文字列を愚直に展開すると2(2(2(2(……))))などという場合に展開しきれなくなる。
mが400以下であることに注目し、
適当な部分だけを展開する。
An(B)Cのような文字列は
A + Bの先頭+ (区切り) + Bの末尾 + B + Bの先頭 + (区切り) + Bの末尾 + C
のように展開すればいい。
ここで、先頭、末尾は400文字までしか持たなくてよい。
そうすれば、一度のn()の展開で、最大で1600文字程度しか増えないので、
展開しきることができる。
区切りは文字列中に絶対に現れない文字を適当に選んで挿入する。
Bが短い場合、
An(B)CのAとCがつながる場合があるので、
上のように区切り文字を入れてしまうと、AとCがつながらなくなってしまう。
なので、n(B)が短い場合(400文字以上だったらつながらない)は、
直接展開するようにする。
文字列を展開しおえたら、
tの全ての部分文字列(400*400/2 = 8万個程度)について、
sに出現するかを調べる。
これはナイーブにやるとTLEなので、sのsuffix arrayを作って適当に高速に検索する。
ソースコード
// Larsson-Sadakane's Suffix array Construction: O(n (log n)^2) struct SAComp { const int h, *g; SAComp(int h, int* g) : h(h), g(g) {} bool operator() (int a, int b) { return a == b ? false : g[a] != g[b] ? g[a] < g[b] : g[a+h] < g[b+h]; } }; int *buildSA(char* t, int n) { int g[n+1], b[n+1], *v = new int[n+1]; rep(i,n+1) v[i] = i, g[i] = t[i]; b[0] = 0; b[n] = 0; sort(v, v+n+1, SAComp(0, g)); for(int h = 1; b[n] != n ; h *= 2) { SAComp comp(h, g); sort(v, v+n+1, comp); rep(i, n) b[i+1] = b[i] + comp(v[i], v[i+1]); rep(i, n+1) g[v[i]] = b[i]; } return v; } // Naive matching O(m log n) int find(char *t, int n, char *p, int m, int *sa) { int a = 0, b = n; while (a < b) { int c = (a + b) / 2; if (strncmp(t+sa[c], p, m) < 0) a = c+1; else b = c; } return strncmp(t+sa[a], p, m) == 0 ? sa[a] : -1; } int n,m,k; string s; char *sc,t[500]; int *sa,len; ll ck(){ ll res=0; for(int l=0,r=0;l<m;l++){ while(r<l)r++; while(r<m){ char tmp=t[r+1]; t[r+1]=0; bool res=find(sc,len,t+l,r-l+1,sa)>=0; t[r+1]=tmp; if(!res)break; r++; } res+=r-l; } return res; } const string rec(int l,int r){ if(l>=r)return ""; if(isdigit(s[l])){ int i,x=0,d=0; for(;l<r&&isdigit(s[l]);l++){ x*=10; x+=s[l]-'0'; x=min(x,400); } for(i=l;i<r;i++){ if(s[i]=='(')d++; if(s[i]==')')d--; if(d==0)break; } string res="", tmp=rec(l+1,i); if(x==1)return tmp; rep(i,x-1){ res+=tmp; if(res.size()>400)break; } const string str=res+tmp; if(str.size()<=800)return str+rec(i+1,r); const string head400=str.substr(0,min((int)str.size(),400)); const string tail400=str.substr(max(0,(int)str.size()-400)); const string tail=tmp.substr(max(0,(int)tmp.size()-400)); const string head=tmp.substr(0,min(400,(int)tmp.size())); return head400+"@"+tail+res+head+"@"+tail400+rec(i+1,r); } return s[l]+rec(l+1,r); } int main(){ cin>>n>>m>>k; cin>>s; s=rec(0,n); len=s.size(); sc=(char*)malloc(len+1); sprintf(sc,"%s",s.c_str()); sa=buildSA(sc,len); int ansi=0; ll ans=0; rep(i,k){ cin>>t; ll tmp=ck(); if(tmp>ans)ans=tmp, ansi=i; } cout<<ansi+1<<" "<<ans<<endl; return 0; }