zoukankan      html  css  js  c++  java
  • 洛谷P4022 熟悉的文章

    题意:给定一个串集合s,每次给定一个串t,询问一个最大的L,使得存在一种划分能把t划分成若干个子串, 其中好的子串总长不小于0.9|t|。好的子串定义为长度不小于L且是s中某一个串的子串。

    解:发现这个L可以二分。如果一个L满足那么小一点的L也满足。考虑如何check。

    可以求最长的总好的子串长度,然后看是否大于0.9。

    这样就能想到DP了。设f[i]表示t[0:i]能划分出的最长好的子串。转移就是考虑第i是否是一个好的子串的结尾。如果是就枚举卡开头,否则就是f[i - 1]。

    这个每个位置都有一个最长能匹配的长度mat[i],然后我们以i结尾的好的子串长度限制就是mat[i] ~ mid。

    我们发现转移过来的两个限制,右边界显然是每次 + 1的,而左边界单调递增。具体来说,如果i结尾的最长匹配是[k, i],那么i - 1结尾的最长匹配不会比[k, i - 1]还短。

    所以用广义SAM搞出来mat,然后二分 + 单调队列优化DP。

    注意开头的时候f[0]是mat[0] >= mid

      1 #include <bits/stdc++.h>
      2 
      3 const int N = 1000010;
      4 const double eps = 1e-8;
      5 
      6 int tr[N][2], fail[N], len[N], tot = 1;
      7 int mat[N], f[N];
      8 int stk[N], top, head;
      9 char str[N];
     10 
     11 inline int Max(const int &a, const int &b) {
     12     return a > b ? a : b;
     13 }
     14 
     15 inline int split(int p, int f) {
     16     int Q = tr[p][f], nQ = ++tot;
     17     len[nQ] = len[p] + 1;
     18     fail[nQ] = fail[Q];
     19     fail[Q] = nQ;
     20     memcpy(tr[nQ], tr[Q], sizeof(tr[Q]));
     21     while(tr[p][f] == Q) {
     22         tr[p][f] = nQ;
     23         p = fail[p];
     24     }
     25     return nQ;
     26 }
     27 
     28 inline int insert(int f, int p) {
     29     if(tr[p][f]) {
     30         int Q = tr[p][f];
     31         if(len[Q] == len[p] + 1) {
     32             return Q;
     33         }
     34         else {
     35             return split(p, f);
     36         }
     37     }
     38     int np = ++tot;
     39     len[np] = len[p] + 1;
     40     while(p && !tr[p][f]) {
     41         tr[p][f] = np;
     42         p = fail[p];
     43     }
     44     if(!p) {
     45         fail[np] = 1;
     46     }
     47     else {
     48         int Q = tr[p][f];
     49         if(len[Q] == len[p] + 1) {
     50             fail[np] = Q;
     51         }
     52         else {
     53             fail[np] = split(p, f);
     54         }
     55     }
     56     return np;
     57 }
     58 
     59 int large[N << 2];
     60 std::bitset<N * 4> tag;
     61 
     62 inline void pushdown(int o) {
     63     if(tag[o]) {
     64         tag.set(o << 1);
     65         tag.set(o << 1 | 1);
     66         large[o << 1] = large[o << 1 | 1] = 0;
     67         tag.reset(o);
     68     }
     69     return;
     70 }
     71 
     72 void change(int p, int v, int l, int r, int o) {
     73     if(l == r) {
     74         tag.reset(o);
     75         large[o] = v;
     76         return;
     77     }
     78     pushdown(o);
     79     int mid = (l + r) >> 1;
     80     if(p <= mid) change(p, v, l, mid, o << 1);
     81     else change(p, v, mid + 1, r, o << 1 | 1);
     82     large[o] = Max(large[o << 1], large[o << 1 | 1]);
     83     return;
     84 }
     85 
     86 int getMax(int L, int R, int l, int r, int o) {
     87     if(L <= l && r <= R) {
     88         return large[o];
     89     }
     90     int mid = (l + r) >> 1, ans = 0;
     91     pushdown(o);
     92     if(L <= mid) ans = getMax(L, R, l, mid, o << 1);
     93     if(mid < R) ans = Max(ans, getMax(L, R, mid + 1, r, o << 1 | 1));
     94     return ans;
     95 }
     96 
     97 int main() {
     98     int n, m;
     99     scanf("%d%d", &n, &m);
    100     for(int i = 1; i <= m; i++) {
    101         scanf("%s", str);
    102         int len = strlen(str), last = 1;
    103         for(int j = 0; j < len; j++) {
    104             last = insert(str[j] - '0', last);
    105         }
    106         memset(str, 0, len * sizeof(char));
    107     }
    108 
    109     for(int A = 1; A <= n; A++) {
    110         scanf("%s", str);
    111         int Len = strlen(str);
    112 
    113         int p = 1, lenth = 0;
    114         for(int i = 0; i < Len; i++) {
    115             int f = str[i] - '0';
    116             while(p && !tr[p][f]) {
    117                 p = fail[p];
    118                 lenth = len[p];
    119             }
    120             if(tr[p][f]) {
    121                 p = tr[p][f];
    122                 lenth++;
    123             }
    124             else {
    125                 p = 1, lenth = 0;
    126             }
    127             mat[i] = lenth;
    128             //printf("mat %d = %d 
    ", i, mat[i]);
    129         }
    130 
    131         int l = 0, r = Len;
    132         while(l < r) {
    133             int mid = (l + r + 1) >> 1;
    134 
    135             f[0] = (mat[0] >= mid); /// ERROR : f[0] = mat[0]
    136             stk[head = top = 1] = 0;
    137             for(int i = 1; i < Len; i++) {
    138                 f[i] = f[i - 1];
    139                 int L = i - mat[i], R = i - mid;
    140                 while(head <= top && f[R] - R >= f[stk[top]] - stk[top]) {
    141                     --top;
    142                 }
    143                 stk[++top] = R;
    144                 while(head < top && stk[head] < L) {
    145                     ++head;
    146                 }
    147                 if(L <= stk[head] && stk[head] <= R) {
    148                     f[i] = Max(f[i], f[stk[head]] + i - stk[head]);
    149                 }
    150             }
    151             //printf("mid = %d  f = %d 
    ", mid, f[Len - 1]);
    152             if(10 * f[Len - 1] >= 9 * Len) {
    153                 l = mid;
    154             }
    155             else {
    156                 r = mid - 1;
    157             }
    158         }
    159         printf("%d
    ", r);
    160         memset(str, 0, Len * sizeof(char));
    161     }
    162 
    163     return 0;
    164 }
    AC代码

    我非常傻,一开始写的是个线段树,没看出来单调性...

  • 相关阅读:
    函数式宏定义与普通函数
    linux之sort用法
    HDU 4390 Number Sequence 容斥原理
    HDU 4407 Sum 容斥原理
    HDU 4059 The Boss on Mars 容斥原理
    UVA12653 Buses
    UVA 12651 Triangles
    UVA 10892
    HDU 4292 Food
    HDU 4288 Coder
  • 原文地址:https://www.cnblogs.com/huyufeifei/p/10808652.html
Copyright © 2011-2022 走看看