Boring counting
\[ Time Limit: 1000 ms \quad Memory Limit: 32768 kB \]
题意
给出一个字符串,求出其中出现两次及以上的子串个数,要求子串之间不可以重合。
思路
对字符串后缀数组,然后枚举子串长度 \(len\),若某一段连续的 \(sa[i]\) 的 \(lcp \geq len\),那么说明这一段内存在一个长度为 \(lcp\) 的子串,而我们只需要其中的前 \(len\) 部分,接下来只要找出这个子串出现的最左和最右位置,然后判断中间能否放下这个长度为 \(len\) 的子串就可以了,如果可以的话,就让 \(ans\)++。
\(Hint\)
这题还让我学到后缀数组的另一个细节,在构建后缀数组之前,要在末尾加上一个比所有字符都小的字符,因为在求 \(height\) 的时候需要判断 \(a[i+k]==a[j+k]\),否则这里可能会无限扩展下去。
/***************************************************************> File Name : a.cpp> Author : Jiaaaaaaaqi> Created Time : 2019年05月23日 星期四 22时40分17秒***************************************************************/#include <map>
#include <set>
#include <list>
#include <ctime>
#include <cmath>
#include <stack>
#include <queue>
#include <cfloat>
#include <string>
#include <vector>
#include <cstdio>
#include <bitset>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define lowbit(x) x & (-x)
#define mes(a, b) memset(a, b, sizeof a)
#define fi first
#define se second
#define pii pair<int, int>typedef unsigned long long int ull;
typedef long long int ll;
const int maxn = 1e5 + 10;
const int maxm = 1e5 + 10;
const ll mod = 1e9 + 7;
const ll INF = 1e18 + 100;
const int inf = 0x3f3f3f3f;
const double pi = acos(-1.0);
const double eps = 1e-8;
using namespace std;int n, m;
int cas, tol, T;char s[maxn];
int a[maxn], sa[maxn], tp[maxn], tax[maxn], rk[maxn], height[maxn];void rsort(int n, int m) {for(int i=0; i<=m; i++) tax[i] = 0;for(int i=1; i<=n; i++) tax[rk[tp[i]]]++;for(int i=1; i<=m; i++) tax[i] += tax[i-1];for(int i=n; i>=1; i--) sa[tax[rk[tp[i]]]--] = tp[i];
}int cmp(int *f, int x, int y, int w) {return f[x]==f[y] && f[x+w]==f[y+w];
}void SA(int *a, int n, int m) {for(int i=1; i<=n; i++) rk[i] = a[i], tp[i] = i;rsort(n, m);for(int w=1, p=1, i; p<n; w<<=1, m=p) {for(p=0, i=n-w+1; i<=n; i++) tp[++p] = i;for(i=1; i<=n; i++) if(sa[i] > w) tp[++p] = sa[i]-w;rsort(n, m), swap(rk, tp);rk[sa[1]] = p = 1;for(i=2; i<=n; i++) rk[sa[i]] = cmp(tp, sa[i], sa[i-1], w) ? p : ++p;}int j, k=0;for(int i=1; i<=n; height[rk[i++]] = k)for(k = k ? k-1 : k, j=sa[rk[i]-1]; a[i+k]==a[j+k]; k++);
}int calc(int len) {int ans = 0;int mx, mn;mx = -inf, mn = inf;for(int i=2; i<=n; i++) {if(height[i]>=len) {mx = max(mx, max(sa[i], sa[i-1]));mn = min(mn, min(sa[i], sa[i-1]));} else {if(mx - mn >= len) ans++;mx = -inf, mn = inf;}}if(mx - mn >= len) ans++;return ans;
}int main() {while(scanf("%s", s+1)) {if(s[1] == '#') break;n = strlen(s+1);s[++n] = 2;for(int i=1; i<=n; i++) {a[i] = s[i];}SA(a, n, 260);// for(int i=1; i<=n; i++) {// printf("sa[%d] = %d\n", i, sa[i]);// }int ans = 0;for(int i=1; i<=(n+1)/2; i++) {ans += calc(i);}printf("%d\n", ans);}return 0;
}