@codeforces - 932G @回文パーティション


@説明@

文字列sを与えられ、偶数のセグメントに分割することができるどのように多くのプログラム尋ねる(P_1、P_2、...、P_K \)\ので(P_I = P_ K-I +。1 {} \)\
10 ^ 9 + 7を成形します。

2≤| S | ≤10 ^ 6。

元のタイトルはここに突きます

@溶液@

回文部門は、古典的なアプローチがあります。しかし、この質問は、さらに、変換する完全回文部門、必要ではありません。
ストリング構成= T \(1-N-s_1s_ns_2s_ {...} \) オリジナルタイトルの有効な区分T回文分割相当の次にデュアル。

DPの定義は、[i]はi番目の分割方式パリンドロームの前に文字の数を表します。分割回文に、私は奇数の直接原因DP [I] = 0である場合にも起因します。
すなわち、パリンドローム構造の単純な搬送ロボット、私はすべて終了して決定し、その後、これらのパリンドローム文字列を転送するパリンドローム配列。

情報の使用の可能性が以前に処理されていることを検討してください。図:

いくつかのパリンドローム配列長dは、演算シーケンス(図黒線)に対する耐性として形成されているがある場合、我々は容易に移動既に算出ID(図青線)の値をとることができます。

特定のパリンドロームオートマトンのそれぞれのノードX、示さD(V)= LEN(V ) -でlen(FA(V))、 D(V)算術級数と呼ばれる同一の連続するセグメント(注ここでは)算術演算の列数を定義します。
最終更新地点x(t)は、ルートパスに対応する接頭辞Tパリンドローム最長のサフィックスは、xは最大深さの点での演算シーケンスのいずれかであることを示しています。
今、各点回文ツリーのメンテナンス時:彼の等差数列に対応するDPの終わりに、その位置に更新され、最後のF。

結論:D(V)= D(FA場合 (V))、 その後、I - D(V)は、FA(V)最後に位置更新です。
証明は2つの部分があります: -最初に、そこ(D(V)、私の証拠ではありません I) FA(v)が更新されなければならないD(v)の場所-私を証明し、次に位置更新FA(V)が、と。
したがって、FA(V)良いFを維持直接アクセスは単に青色で示されています。
パリンドローム配列の黒い部分があることに注意が青色([I]上記配列弛み)の一部ではなく、プロセスを分離する必要があります。

別の結論:すべてのO(ログ)番目の演算シーケンスを形成するパリンドローム配列パリンドローム配列。
したがって、回文サフィックス文字列の境界線、すなわちパリンドローム、とは直接関係の国境サイクルを証明することができます。
第一の方向D(X)と現在のD(V)を維持することが示さスラック(V)= X、点に等しくありません。このような直接ジャンプたるみは倍のルートへのログオンにジャンプアップ。

なお、詳細:ポイントFA =スラックが、それはFA貢献としてカウントされるべきではありません。

@acceptedコード@

#include <cstdio>
#include <cstring>
const int MAXN = 1000000;
const int MOD = int(1E9) + 7;
struct node{
    int len, dif, f;
    node *slk, *ch[26], *fa;
}pl[MAXN + 5], *rt1, *rt2, *ncnt;
node *nd[MAXN + 5];
void build(char *S, int len) {
    rt1 = ncnt = pl, rt2 = (++ncnt);
    rt2->fa = rt1, rt1->len = -1, rt2->len = 0;
    node *pre = rt1;
    for(int i=0;i<len;i++) {
        while( S[i] != S[i - pre->len - 1] )
            pre = pre->fa;
        if( pre->ch[S[i] - 'a'] == NULL ) {
            node *q = (++ncnt);
            q->len = pre->len + 2;
            if( pre == rt1 )
                q->fa = rt2;
            else {
                node *p = pre->fa;
                while( S[i] != S[i - p->len - 1 ] )
                    p = p->fa;
                q->fa = p->ch[S[i] - 'a'];
            }
            q->dif = q->len - q->fa->len;
            q->slk = (q->dif == q->fa->dif ? q->fa->slk : q->fa);
            pre->ch[S[i] - 'a'] = q;
        }
        nd[i+1] = pre = pre->ch[S[i] - 'a'];
    }
}
char s[MAXN + 5], t[MAXN + 5];
int dp[MAXN + 5];
int main() {
    scanf("%s", s);
    int len = strlen(s);
    for(int i=0;i<len/2;i++)
        t[(i<<1) + 1] = s[i], t[(i<<1|1) + 1] = s[len-i-1];
    build(t + 1, len);
    dp[0] = 1;
    for(int i=1;i<=len;i++) {
        node *p = nd[i];
        while( p != rt2 ) {
            p->f = dp[i - p->slk->len - p->dif];
            if( p->slk != p->fa )
                p->f = (p->f + p->fa->f) % MOD;
            dp[i] = (dp[i] + p->f) % MOD;
            p = p->slk;
        }
        if( i & 1 ) dp[i] = 0;
    }
    printf("%d\n", dp[len]);
}

詳細@

キーコードは、非常に長いではありません。

すべてが書き込みパリティ回文文字列ビットを見つけることは困難やって議論を分けていました。その後、直接DP値0に強制されることが判明。

おすすめ

転載: www.cnblogs.com/Tiw-Air-OAO/p/12024499.html