1. 程式人生 > >洛谷P4022 熟悉的文章

洛谷P4022 熟悉的文章

play ac代碼 長度限制 開頭 har clas 註意 hang get

題意:給定一個串集合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代碼

我非常傻,一開始寫的是個線段樹,沒看出來單調性...

洛谷P4022 熟悉的文章