這道題不難想到這樣的dp。c++
dp[字符串si] = 以si爲結尾的最大總權值。ui
dp[si] = max(dp[sj]) ,1.j < i,2.sj是si的子串。spa
對於第二個條件,是一個多模版串匹配的問題,能夠用AC自動機。指針
預先O(m)把AC自動機建好,而後動態更新AC自動機上的dp值,code
匹配的時候,指向字符的指針移動總共是O(m),blog
而每一個單詞,fail指針走尋找後綴倒是O(m),即便改爲後綴連接也是O(n)。too slow!字符串
找到一個單詞後,須要避免找後綴,動態維護這個單詞的dp值。get
一開始全部單詞的dp都是0。string
更新的時候,dp[si]須要更新全部dp[sj],其中si是sj的後綴。it
若是父節點是子節點的後綴,把全部的單詞(包括空後綴)鏈接起來將會獲得以空字符串爲根的後綴連接樹。
這樣就變成一個更新子樹的問題,dfs把樹形轉成線性之後能夠用線段樹來維護。
詢問單點最大值,區間更新O(logn)。
複雜度O(mlogn)
潛在的坑點:
1.Trie個結點可能對應多個單詞,若是隻更新了其中一個單詞的線性區間RE...(map,前向鏈表,vector均可以搞
/********************************************************* * ------------------ * * author AbyssFish * **********************************************************/ #include<cstdio> #include<cstring> #include<queue> #include<algorithm> using namespace std; const int LEN = 3e5+5; const int MAXN = 2e4+5; int W[MAXN], S[MAXN]; int N; char s[LEN]; int hd[LEN]; int nx[MAXN], to[MAXN], ec; void add_e(int u,int v) { to[ec] = v; nx[ec] = hd[u]; hd[u] = ec++; } #define eachedge int i = hd[u]; ~i; i = nx[i] inline void init_g(int n){ memset(hd,-1,n<<2); ec = 0; } int L[MAXN], R[MAXN], dfs_clk; //string's linear suffix link tree id const int ST_SIZE = 1<<16; int dp[ST_SIZE]; #define para int o = 1,int l = 0,int r = dfs_clk #define lo (o<<1) #define ro (o<<1|1) #define Tvar int md = (l+r)>>1; #define lsn lo,l,md #define rsn ro,md,r #define insd ql<=l&&r<=qr void build(para) { dp[o] = 0; if(r-l>1){ Tvar build(lsn); build(rsn); } } void update(int ql,int qr,int v,para) { if(insd){ dp[o] = max(dp[o],v); } else { Tvar if(ql < md) update(ql,qr,v,lsn); if(qr > md) update(ql,qr,v,rsn); } } int query(int p,para) { int re = 0; while(r-l>1){ Tvar if(p<md){ o = lo; r = md; } else { o = ro; l = md; } re = max(re,dp[o]); } return re; } const int sigma_size = 26, MAXND = LEN; struct AhoCorasick_automata { #define idx(x) (x-'a') int ch[MAXND][sigma_size]; int f[MAXND]; int last[MAXND]; int cnt; int val[MAXND]; int nx_val[MAXN]; void add_v(int o,int x) { nx_val[x] = val[o]; val[o] = x; } int newNode() { int i = ++cnt; memset(ch[i],0,sizeof(ch[i])); val[i] = 0; return i; } void init() { cnt = -1; newNode(); } int add(char *s,int id) { int u = 0, i, c; for(i = 0; s[i]; i++){ c = idx(s[i]); if(!ch[u][c]){ ch[u][c] = newNode(); } u = ch[u][c]; } add_v(u,id); return i; } queue<int> q; void getFail() { int u, c, v, r; //f[0] = 0; last[0] = 0; for(c = 0; c < sigma_size; c++){ u = ch[0][c]; if(u){ q.push(u); f[u] = 0; last[u] = 0; } } while(!q.empty()){ r = q.front(); q.pop(); for(c = 0; c < sigma_size; c++){ u = ch[r][c]; if(u){ q.push(u); v = f[u] = ch[f[r]][c]; last[u] = val[v] ? v : last[v]; } else ch[r][c] = ch[f[r]][c]; } } } void dfs(int u) { int le = dfs_clk++; for(eachedge){ dfs(to[i]); } int ri = dfs_clk; for(int id = val[u]; id; id = nx_val[id]){ L[id] = le; R[id] = ri; } } void buildTree() { init_g(cnt+1); for(int u = 1; u <= cnt; u++)if(val[u]){ add_e(last[u],u); } dfs_clk = 0; dfs(0); } void work() { int i,j,c,u,id; int ans = 0, mx; build(); for(i = 1; i <= N; i++){ u = 0; mx = 0; for(j = S[i-1]; j < S[i]; j++){ c = idx(s[j]); u = ch[u][c]; if(val[u]){ id = val[u]; mx = max(mx, query(L[id])); } else if(last[u]){ id = val[last[u]]; mx = max(mx, query(L[id])); } } if(W[i] > 0){ ans = max(ans, mx += W[i]); update(L[i],R[i],mx); } } printf("%d\n",ans); } }ac; void solve() { scanf("%d",&N); ac.init(); for(int i = 1; i <= N; i++){ scanf("%s%d",s+S[i-1],W+i); S[i] = ac.add(s+S[i-1],i)+S[i-1]; } ac.getFail(); ac.buildTree(); ac.work(); } //#define LOCAL int main() { #ifdef LOCAL freopen("data.txt","r",stdin); #endif int T, kas = 0; scanf("%d",&T); while(++kas <= T){ printf("Case #%d: ",kas); solve(); } return 0; }