hdu 4117 AC自动机DP + 线段树优化

problem:

给你N个字符串, N(1 <= N <= 2 * 104), 所有穿的长度加一起不超过 3 * 105.每个串有个值。这个值[-1000, 1000].

问不打乱字符串顺序,从中取若干个字符串,使得前一个串是后一个串的子串,求满足前面调条件的字符串值得和最大,求这个值。


think:

建立AC自动机。后面检查取了以每个字符串是最后取的串的最大值。那么检查第i个字符串的时候,就是这个字符串的未节点前面所有的fail指针一直到根节点和他的上面节点的最大值,加上第i个字符串的值。直接这样暴力的话,是N*M,一定会超时的。

由于每个节点一直fail都会指向根节点,所以把他转化为一棵fail树。我们发现当改变fail树里面的一个节点时,会影响到的是他的子孙,那么就可以用时间戳来把fail树变成线性的从而可以用线段树进行优化。

可是除了fail影响一个节点的还有trie树里面的父节点。所以我们顺着所有的字符扫一遍,存一下上一个走到的结点就是他的trie树中的父节点。和fail树的线段树中比较一下就可以了。

这题建了三棵树,trie树,fail树,线段树。vetor存树耗内存,把所有long long 都改成int才从mle变成ac。


code:

  1. const int kind = 26;  
  2. const int NN = 333333;  
  3.   
  4. int cnt;//ac状态数  
  5. char str[NN];//所有的字符接在一起  
  6. int pos[NN];  
  7.   
  8. int fail[NN];//每个状态的fail指针  
  9. int child[NN][kind];//trie树  
  10. int ans;//最终答案  
  11.   
  12. int val[NN];//每个单词输入的value  
  13.   
  14. int tal;//时间戳状态数  
  15. vector<int>v[NN];//fail树  
  16. int in[NN];//时间戳  
  17. int out[NN];  
  18. int mx[NN<<2];//线段树  
  19.   
  20. inline int newNode(){  
  21.     ++cnt;  
  22.     for(int i=0; i<kind; ++i) child[cnt][i] = -1;  
  23.     return cnt;  
  24. }  
  25.   
  26. void insert(char *str, int root, int id){  
  27.     int p = root;  
  28.     int len = strlen(str);  
  29.     for(int i=0; i < len; ++i){  
  30.         int k = str[i] - 'a';  
  31.         if(child[p][k] == -1) child[p][k] = newNode();  
  32.         p = child[p][k];  
  33.     }  
  34. }  
  35.   
  36. void build_fail(int root){  
  37.     queue<int>q;  
  38.     int p = root;  
  39.     q.push(p);  
  40.     while(!q.empty()){  
  41.         p = q.front();  
  42.         q.pop();  
  43.         if(p) v[fail[p]].push_back(p);  
  44.         for(int k = 0; k < kind; ++k){  
  45.             int tmp = child[p][k];  
  46.             if(tmp != -1){  
  47.                 if(p!=root) fail[tmp] = child[fail[p]][k];  
  48.                 else fail[tmp] = root;  
  49.                 q.push(tmp);  
  50.             } else {  
  51.                 if(p!=root) child[p][k] = child[fail[p]][k];  
  52.                 else child[p][k] = root;  
  53.             }  
  54.         }  
  55.     }  
  56. }  
  57.   
  58. void dfs(int s){  
  59.     in[s] = ++tal;  
  60.     int len = v[s].size();  
  61.     for(int i = 0; i < len; ++i){  
  62.         dfs(v[s][i]);  
  63.     }  
  64.     out[s] = tal;  
  65.     v[s].clear();  
  66. }  
  67.   
  68. int query(int l, int r, int k, int L){  
  69.     if(l==L && r==L) return mx[k];  
  70.     int mid = (l+r)>>1;  
  71.     mx[k<<1] = max(mx[k<<1], mx[k]);  
  72.     mx[k<<1|1] = max(mx[k<<1|1], mx[k]);  
  73.     if(L<=mid) return query(l, mid, k<<1, L);  
  74.     else return query(mid+1, r, k<<1|1, L);  
  75. }  
  76.   
  77. void update(int l, int r, int k, int L, int R, int x){  
  78.     if(mx[k] >= x) return;  
  79.     if(l==L && r==R){  
  80.         mx[k] = x;  
  81.         return;  
  82.     }  
  83.     int mid = (l+r)>>1;  
  84.     if(R<=mid) update(l, mid, k<<1, L, R, x);  
  85.     else if(L>mid) update(mid+1, r, k<<1|1, L, R, x);  
  86.     else{  
  87.         update(l, mid, k<<1, L, mid, x);  
  88.         update(mid+1, r, k<<1|1, mid+1, R, x);  
  89.     }  
  90. }  
  91.   
  92. int main(){  
  93.     int n, m, tt=0;  
  94.     scanf("%d", &m);  
  95.     while(m--){  
  96.         scanf("%d", &n);  
  97.         cnt = -1;  
  98.         int root = newNode();  
  99.         pos[0] = 0;  
  100.         for(int i = 1; i <= n; ++i){  
  101.             scanf("%s%d", str + pos[i-1], &val[i]);  
  102.             insert(str + pos[i-1], root, i);  
  103.             pos[i] = pos[i-1] + strlen(str + pos[i-1]);  
  104.         }  
  105.         build_fail(root);  
  106.         tal = 0;  
  107.         dfs(0);  
  108.         memset(mx, 0, sizeof(mx));  
  109.         ans = 0;  
  110.         for(int i = 1; i <= n; ++i){  
  111.             int p = 0;  
  112.             int preTmp = 0;  
  113.             for(int j = pos[i-1]; j < pos[i]; ++j){//这里虽然是两层循环,其实一共只有pos[n]  
  114.                 int v = val[i] * (j==pos[i]-1);  
  115.                 p = child[p][str[j]-'a'];  
  116.                 int tmp = query(1, tal, 1, in[p]);  
  117.                 tmp = max(preTmp, tmp) + v;  
  118.                 ans = max(ans, tmp);  
  119.                 update(1, tal, 1, in[p], out[p], tmp);  
  120.                 preTmp = tmp;  
  121.             }  
  122.         }  
  123.         printf("Case #%d: ", ++tt);  
  124.         cout<<ans<<endl;  
  125.     }  
  126.     return 0;  
  127. }  
  128. /* 
  129. 100 
  130. 5 
  131. a 1 
  132. ab 2 
  133. abb 3 
  134. baba 5 
  135. abbab 8 
  136.  
  137. 5 
  138. a 1 
  139. b 1 
  140. ab 3 
  141. aabb 3 
  142. abb 10 
  143.  
  144. 2 
  145. aa 1 
  146. a 1 
  147.  
  148. 4 
  149. a 1 
  150. aab 10 
  151. bab 1 
  152. aabab 1 
  153.  
  154. 4 
  155. a 1 
  156. aab 1 
  157. bab 10 
  158. aabab 1 
  159. */  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值