A + B Problem 【LibreOJ - 6158】【exkmp后缀数组DC3】

    技术2022-07-15  48

    题目链接


      本题我们可以考虑成从后往前的匹配,因为要让后缀最多的为0,不妨就是从后往前按照需求的进行匹配即可,这个很好想,就是细节有点多了。

      先是讲讲后缀数组的做法,这里不能用倍增的后缀数组,因为还要带一个log,这样要是写的不好可就TLE了,所以这里用线性复杂度的DC3来写这道题,然后求出height,找到需求串的位置。然后按照sa函数的排名从前往后和从后往前的分别去考虑,进行相同前缀的计算,然后算出对应的前缀的长度和最多允许的长度,然后再看看如果一边是满了的时候,可不可以9或者0的进位(如果前面没有进位的时候,就是找0)。

    #include <iostream> #include <cstdio> #include <cmath> #include <string> #include <cstring> #include <algorithm> #include <limits> #include <vector> #include <stack> #include <queue> #include <set> #include <map> #include <bitset> //#include <unordered_map> //#include <unordered_set> #define lowbit(x) ( x&(-x) ) #define pi 3.141592653589793 #define e 2.718281828459045 #define INF 0x3f3f3f3f #define HalF (l + r)>>1 #define lsn rt<<1 #define rsn rt<<1|1 #define Lson lsn, l, mid #define Rson rsn, mid+1, r #define QL Lson, ql, qr #define QR Rson, ql, qr #define myself rt, l, r #define MP(a, b) make_pair(a, b) using namespace std; typedef unsigned long long ull; typedef unsigned int uit; typedef long long ll; const int maxN = 6e6 + 10; struct DC3 { static const int maxn = 6e6 + 10; #define F(x) ((x)/3+((x)%3==1?0:tb)) #define G(x) ((x)<tb?(x)*3+1:((x)-tb)*3+2) int sa[maxN], rk[maxN], height[maxN], s[maxN]; int wa[maxn], wb[maxn], wv[maxn], wss[maxn]; inline int c0(int *r, int a, int b) { return r[a]==r[b] && r[a+1]==r[b+1] && r[a+2]==r[b+2]; } inline int c12(int k, int *r, int a, int b) { if(k==2) return r[a]<r[b] || (r[a]==r[b] && c12(1,r,a+1,b+1)); else return r[a]<r[b] || (r[a]==r[b] && wv[a+1] < wv[b+1]); } inline void ssort(int *r, int *a, int *b, int n, int m) { int i; for(i=0; i<n; i++) wv[i] = r[a[i]]; for(i=0; i<m; i++) wss[i] = 0; for(i=0; i<n; i++) wss[wv[i]]++; for(i=1; i<m; i++) wss[i] += wss[i-1]; for(i=n-1; i>=0; i--) b[--wss[wv[i]]] = a[i]; } inline void dc3(int *r, int *sa, int n, int m) { int i, j, *rn = r + n; int *san = sa + n, ta = 0, tb = (n + 1) / 3, tbc = 0, p; r[n] = r[n + 1] = 0; for(i=0;i<n;i++) if(i % 3 != 0) wa[tbc++] = i; ssort(r+2, wa, wb, tbc, m); ssort(r+1, wb, wa, tbc, m); ssort(r, wa, wb, tbc, m); for(p=1, rn[F(wb[0])] = 0, i = 1; i < tbc; i++) rn[F(wb[i])] = c0(r, wb[i-1], wb[i]) ? p - 1 : p++; if(p < tbc) dc3(rn, san, tbc, p); else for(i=0; i<tbc; i++) san[rn[i]] = i; for(i=0; i<tbc; i++) if(san[i]<tb) wb[ta++] = san[i] * 3; if(n % 3 == 1) wb[ta++] = n - 1; ssort(r, wb, wa, ta, m); for(i=0; i<tbc; i++) wv[wb[i]=G(san[i])]=i; for(i=0, j=0, p=0; i<ta && j<tbc; p++) sa[p]=c12(wb[j]%3, r, wa[i], wb[j]) ? wa[i++] : wb[j++]; for(; i<ta; p++) sa[p] = wa[i++]; for(; j<tbc; p++) sa[p] = wb[j++]; } inline void da(int n,int m) { for(int i=n; i<n*3; i++) s[i]=0; dc3(s, sa, n+1, m); int i, j, k=0; for(i=0;i<=n;i++) rk[sa[i]]=i; for(i=0;i<n;i++){ if(k)k--; j=sa[rk[i]-1]; while(s[i+k] == s[j+k])k++; height[rk[i]]=k; } } } sa; char s[maxN]; int N, a[maxN], b[maxN], ans, num0[maxN], num9[maxN]; bool zp[maxN]; int main() { while(scanf("%s", s + 1) != EOF) { int len = (int)strlen(s + 1); for(int i=1; i<=len; i++) a[i] = s[len - i + 1] - '0'; num0[len + 1] = num9[len + 1] = 0; for(int i=len; i>=1; i--) { if(a[i] == 0) num0[i] = num0[i + 1] + 1; else num0[i] = 0; if(a[i] == 9) num9[i] = num9[i + 1] + 1; else num9[i] = 0; } bool zero = true; b[1] = 10 - a[1]; if(b[1] == 10) b[1] = 0; else zero = false; zp[1] = zero; for(int i=2; i<=len; i++) { if(zero) { b[i] = 10 - a[i]; if(b[i] == 10) b[i] = 0; else zero = false; } else b[i] = 9 - a[i]; zp[i] = zero; } sa.s[0] = 12; for(int i=1; i<=len; i++) { sa.s[i] = a[i] + 1; } for(int i=1; i<=len; i++) { sa.s[len + i] = b[i] + 1; } N = len << 1; sa.s[N + 1] = 12; sa.s[N + 2] = 12; sa.da(N + 2, 15); // for(int i=1; i<=N; i++) printf("%d%c", sa.sa[i], i == N ? '\n' : ' '); // for(int i=1; i<=N; i++) printf("%d%c", sa.rk[i], i == N ? '\n' : ' '); // for(int i=1; i<=N; i++) printf("%d%c", sa.height[i], i == N ? '\n' : ' '); int mid_rk = sa.rk[len + 1]; ans = 0; for(int i=mid_rk + 1, H = INF, id, id_pre, id_suff, len_pre, len_suff, tmp; i <= N; i++) { H = min(H, sa.height[i]); if(!H) break; id = sa.sa[i]; if(id > len) continue; id_pre = H; id_suff = id; len_suff = len - id + 1; len_pre = len - len_suff; tmp = min(H, min(len_pre, len_suff)); if(tmp == min(len_pre, len_suff)) { if(len_pre > len_suff) { if(zp[len_suff]) tmp += num0[len_suff + 1]; else tmp += min(num9[len_suff + 1], id - tmp - 1); } else if(len_suff > len_pre) { if(zp[len_pre]) tmp += num0[len_pre + len_pre + 1]; else tmp += num9[len_pre + len_pre + 1]; } } ans = max(ans, tmp); } for(int i=mid_rk - 1, H = sa.height[mid_rk], id, id_pre, id_suff, len_pre, len_suff, tmp; i >= 1; i--) { id = sa.sa[i]; if(id > len) continue; id_pre = H; id_suff = id; len_suff = len - id + 1; len_pre = len - len_suff; tmp = min(H, min(len_pre, len_suff)); if(tmp == min(len_pre, len_suff)) { if(len_pre > len_suff) { if(zp[len_suff]) tmp += num0[len_suff + 1]; else tmp += min(num9[len_suff + 1], id - tmp - 1); } else if(len_suff > len_pre) { if(zp[len_pre]) tmp += num0[len_pre + len_pre + 1]; else tmp += num9[len_pre + len_pre + 1]; } } ans = max(ans, tmp); H = min(H, sa.height[i]); if(!H) break; } printf("%d\n", ans); } return 0; } /* 2872 ans:2 19919 ans:3 91991 ans:3 9199 ans:1 901 ans:1 9901 ans:2 9911 ans:1 2018 ans:1 109 ans:1 9091 ans:2 12080 ans:2 1000 ans:2 9545455 ans:4 95454545455 ans:6 10090 ans:2 15999841 ans:5 */

      再讲讲exkmp的做法,如果使用扩展kmp的话,我们可以先去一样的处理出本串对于查询串(也就是需求串的)的最大匹配,也就是每个后缀的最长前缀长度,ex数组。然后与后缀数组相同的来去进行判断。

    #include <iostream> #include <cstdio> #include <cmath> #include <string> #include <cstring> #include <algorithm> #include <limits> #include <vector> #include <stack> #include <queue> #include <set> #include <map> #include <bitset> #include <unordered_map> #include <unordered_set> #define lowbit(x) ( x&(-x) ) #define pi 3.141592653589793 #define e 2.718281828459045 #define INF 0x3f3f3f3f #define eps 1e-6 #define HalF (l + r)>>1 #define lsn rt<<1 #define rsn rt<<1|1 #define Lson lsn, l, mid #define Rson rsn, mid+1, r #define QL Lson, ql, qr #define QR Rson, ql, qr #define myself rt, l, r using namespace std; typedef unsigned long long ull; typedef unsigned int uit; typedef long long ll; //const int maxN = 100; const int maxN = 1e6 + 7; char A[maxN], B[maxN]; bool Zop[maxN]; int sa[maxN], sb[maxN]; int lena, lenb, num0[maxN], num9[maxN]; int p[maxN], ex[maxN]; //p数组是用来让B串自己匹配自己的 void exkmp() { p[1] = lenb; int x = 1; while(sb[x] == sb[x + 1] && x + 1 <= lenb) x++; //因为我们p[1]是具有一定性,所以我们不能直接用,所以要先暴力求出p[2] p[2] = x - 1; int k = 2; for(int i=3; i<=lenb; i++) { int pp = k + p[k] - 1, L = p[i - k + 1]; //pp实际上是p if(i + L < pp + 1) p[i] = L;//i-k+L<pp-k+1化简后i+L<pp else { int j = pp - i + 1; if(j < 0) j = 0; while(sb[j + 1] == sb[i + j] && i + j <= lenb) j++; p[i] = j; k = i; } } x = 1; while(sa[x] == sb[x] && x <= lenb) x++;//ex[1]并不具有一定性,所以我们暴力求出ex[1] ex[1] = x - 1; k = 1; for(int i=2; i<=lena; i++) { int pp = k + ex[k] - 1, L = p[i - k + 1]; if(i + L < pp + 1) ex[i] = L; else { int j = pp - i + 1; if(j < 0) j = 0; while(sb[j + 1] == sa[i + j] && i + j <= lena && j <= lenb) j++; ex[i] = j; k = i; } } } int main() { while(scanf("%s", A + 1) != EOF) { lena = (int)strlen(A + 1); lenb = lena; for(int i=1; i<=lena; i++) { sa[i] = A[lena + 1 - i] - '0'; } for(int i=lena, pos0 = lena, pos9 = lenb; i>=1; i--) { if(sa[i] == 0) { pos9 = i - 1; } else if(sa[i] == 9) { pos0 = i - 1; } else { pos0 = pos9 = i - 1; } num0[i] = pos0 - i + 1; num9[i] = pos9 - i + 1; } bool zero = true; sb[1] = 10 - sa[1]; if(sb[1] == 10) sb[1] = 0; else zero = false; for(int i=2; i<=lenb; i++) { if(zero) { sb[i] = 10 - sa[i]; if(sb[i] == 10) sb[i] = 0; else zero = false; } else { sb[i] = 9 - sa[i]; } Zop[i] = zero; } exkmp(); // printf("bp: "); for(int i=1; i<=lenb; i++) printf("%d%c", p[i], i == lenb ? '\n' : ' '); // printf("ex: "); for(int i=1; i<=lena; i++) printf("%d%c", ex[i], i == lena ? '\n' : ' '); int ans = 0; for(int i=2, len_pre, len_suff; i<=lena; i++) { len_pre = ex[i]; len_pre = min(len_pre, i - 1); len_suff = lena - i + 1; if(len_pre < i - 1) { if(len_pre >= len_suff) { ans = max(ans, len_pre + min(num9[len_pre + 1], i - 1 - len_pre)); } else { ans = max(ans, len_pre); } } else { if(i + len_pre == lena + 1) ans = max(ans, len_pre); else if(Zop[i + len_pre - 1]) { ans = max(ans, len_pre + num0[i + len_pre]); } else { ans = max(ans, len_pre + num9[i + len_pre]); } } } printf("%d\n", ans); } return 0; } /* 15999841 ans:5 9901 ans:2 95454545455 ans:6 */

     

    Processed: 0.010, SQL: 9