洛谷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 \n", 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 \n", 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\n", r);
160         memset(str, 0, Len * sizeof(char));
161     }
162 
163     return 0;
164 }
AC代码

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

猜你喜欢

转载自www.cnblogs.com/huyufeifei/p/10808652.html