相似的子串【后缀数组+二分答案】
相似的子串
http://www.nowcoder.com/questionTerminal/4b30104bfedb4a069fe3ab75907029fd
求K个不相交字符子串的最大相同前缀长度x。
很容易往后缀数组上靠,但是这还不够,因为很容易就想偏了,这里,我们想处理一个是不重叠,一个是最大的前缀相同,于是,不妨设最长前缀为x,然后二分这个x,这是因为height的关系具有连续性,所以这样就能很清晰的划分出来我们需要进行处理的sa的区间了。
然后我们对于这些后缀的前缀下标,我们存进一个升序堆内,贪心来选,看看能否有K个以上的满足条件的不重叠子串,于是二分的判断条件也就写出来了。
注意的是,判断二分x==0的时候,直接返回true,或者从1开始判断。
最后,欢迎来我的博客食用呀:https://blog.csdn.net/qq_41730082/article/details/105447119
#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 0x3f3f3f3f3f3f3f3f #define eps 1e-4 #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 = 2e5 + 7; struct SA { int n, m; int s[maxN]; int y[maxN], x[maxN], c[maxN], sa[maxN], rk[maxN], height[maxN]; inline void get_SA() { for(int i=1; i<=m; i++) c[i] = 0; //桶的初始化 for(int i=1; i<=n; i++) ++c[x[i] = s[i]]; for(int i=2; i<=m; i++) c[i] += c[i - 1]; //利用差分前缀和的思想知道每个关键字最多是在第几名 for(int i=n; i>=1; i--) sa[c[x[i]]--] = i; for(int k=1; k<=n; k<<=1) { int num = 0; for(int i=n - k + 1; i<=n; i++) y[++num] = i; for(int i=1; i<=n; i++) if(sa[i] > k) y[++num] = sa[i] - k; //是否可以作为第二关键字 for(int i=1; i<=m; i++) c[i] = 0; for(int i=1; i<=n; i++) c[x[i]]++; //因为上一次循环已经求出这次的第一关键字了 for(int i=2; i<=m; i++) c[i] += c[i - 1]; for(int i=n; i>=1; i--) //在同一第一关键字下,按第二关键字来排 { sa[c[x[y[i]]]--] = y[i]; y[i] = 0; } swap(x, y); x[sa[1]] = 1; num = 1; for(int i=2; i<=n; i++) { x[sa[i]] = (y[sa[i]] == y[sa[i - 1]] && y[sa[i] + k] == y[sa[i - 1] + k]) ? num : ++num; } if(num == n) break; m = num; } } inline void get_height() { int k = 0; for(int i=1; i<=n; i++) rk[sa[i]] = i; for(int i=1; i<=n; i++) { if(rk[i] == 1) continue; //第一名的height为0 if(k) k--; //height[i] >= height[i - 1] - 1 int j = sa[rk[i] - 1]; while(j + k <= n && i + k <= n && s[i + k] == s[j + k]) k++; height[rk[i]] = k; } } inline void clear() { n = 0; m = 200; } } sa; int N, K; char ch[maxN]; priority_queue<int, vector<int> , greater<int> > Q; inline bool check(int lim) { while(!Q.empty()) Q.pop(); int num = 0, ith = 2, id_1, id_2; Q.push(sa.sa[1]); while(ith <= N + 1) { if(sa.height[ith] < lim) { num = 1; id_1 = Q.top(); Q.pop(); while(!Q.empty()) { id_2 = Q.top(); Q.pop(); if(id_2 - id_1 >= lim) { num++; id_1 = id_2; } } if(num >= K) return true; } Q.push(sa.sa[ith]); ith++; } return false; } int main() { sa.clear(); scanf("%d%d", &N, &K); scanf("%s", ch); for(int i=0; i<N; i++) sa.s[++sa.n] = ch[i]; sa.s[++sa.n] = 'z' + 1; sa.get_SA(); sa.get_height(); // 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.height[i], i == N ? '\n' : ' '); int ans = 0; int L = 1, R = N / K, mid = 0; while(L <= R) { mid = (L + R) >> 1; if(check(mid)) { L = mid + 1; ans = mid; } else R = mid - 1; } printf("%d\n", ans); return 0; } /* 9 3 aaaaaaaaa ans:3 11 3 abababababc ans:2 9 2 dsaasfjai ans:1 */