これも何とか自力回答。
http://code-formula-2014-final.contest.atcoder.jp/tasks/code_formula_2014_final_e
問題
文字列は以下のように定義される。
- かつの時
- kが偶数なら
- kが奇数なら
である、ある文字列Sが与えられるのでn,kを求めよ。
解法
におけるaとbの数はnで決まり、kに依存しない。また文字列長もnで決まる。
(そもそもの文字列長はフィボナッチ数列になる)
よってSの文字列長からnは容易に求められる。
あとはkを総当たりする。
と仮定してをとで再帰的に分割していき、a,bの数がSの部分文字列と一致するか判定していく。
ただ、下記のコードでは高速化のためn≦11の範囲はを全パターン列挙し、直接一致判定をして再帰をそこで終了している。
string S; string FF[12][5000]; int F[3][55]; int num[2][20001]; int okok(int level,int k,int offset) { if(level<10) return S.substr(offset,FF[level][k].size())==FF[level][k]; int len=F[2][level]; if(num[0][offset+len]-num[0][offset]!=F[0][level]) return 0; if(num[1][offset+len]-num[1][offset]!=F[1][level]) return 0; if(k%2) return okok(level-2,k/4,offset) && okok(level-1,k/2,offset+F[2][level-2]); else return okok(level-1,k/2,offset) && okok(level-2,k/4,offset+F[2][level-1]); } void solve() { int i,j,k,l,r,x,y; string s; cin>>S; if(S=="a") return _P("2 0\n"); if(S=="b") return _P("1 0\n"); FOR(i,S.size()) num[0][i+1]=num[0][i]+(S[i]=='b'); FOR(i,S.size()) num[1][i+1]=num[1][i]+(S[i]=='a'); FF[1][0]="b"; FF[2][0]="a"; for(i=3;i<=11;i++) FOR(k,1<<(i-2)) { if(k%2) FF[i][k]=FF[i-2][k/4]+FF[i-1][k/2]; else FF[i][k]=FF[i-1][k/2]+FF[i-2][k/4]; } F[0][1]=F[2][1]=1; F[1][2]=F[2][2]=1; for(i=3;i<=25;i++) F[0][i]=F[0][i-1]+F[0][i-2]; for(i=3;i<=25;i++) F[1][i]=F[1][i-1]+F[1][i-2]; for(i=3;i<=25;i++) F[2][i]=F[2][i-1]+F[2][i-2]; for(x=3;x<=35;x++) if(F[2][x]==S.size()) break; FOR(y,1<<(x-2)) if(okok(x,y,0)) return _P("%d %d\n",x,y); }
まとめ
aとbの数で簡易判定することには割とさっくり到達できたので良かった。