题意:给出一串包含NC个不同个字符的字符串。问有多少个长度为N且不相同的子串。
思路:字符串Hash.将每个长度为N的子串hash成一个数。通过一个数组记录该数是否存在,来完成统计不同子串的个数。
坑:题目中没有任何数据的信息,只告诉你了:所有可能的子串的个数不会超过16000000.那这句话什么意思呢?因为前面告诉你了,整个字符串最多会有NC个不同的字符,其实这句话是说:如果用NC这个数当做进制数去Hash每个长度为N的子串,最终得到的结果不会大于16000000。如果你没有理解这句话的话,可能会像我,在第一遍,用自己定义的hash函数,开一个set去统计是否出现过。这是nlgn的复杂度,当然TLE。所以我们能够开一个数组去存是否存在,把复杂度降到O(n);
代码如下:
#include <cstdio>
#include <cstring>
using namespace std;
const int MAX = 16000000;
int N,NC;
bool used[MAX];
char str[MAX];
int m[256];
int sum;
int cnt;
int main(void)
{
//freopen("input.txt","r",stdin);
int sum = 0;
int high = 1;
int ans = 0;
scanf("%d %d", &N,&NC);
scanf("%s",str);
int n = strlen(str);
int cnt = 0;
for(int i = 0; i < n; ++i){
if(m[str[i]] == 0)
m[str[i]] = cnt++;
if(cnt == NC) break;
}
for(int i = 0 ; i < N-1; ++i)
sum = sum * NC + m[str[i]], high *= NC;
for(int i = 0; i + N - 1 < n; ++i){
sum %= high;
sum = sum * NC + m[str[i + N - 1]];
if(!used[sum]){
used[sum] = true;
ans++;
}
}
printf("%d\n",ans);
return 0;
}
这道题的还可以用后缀数组:对于height数组从前向后考虑,当前的i,它起始位置是sa[i],和前面的字符串的最长公共前缀是height[i],如果height[i] < N 且 sa[i] + N - 1 < length,那这个位置就可以出现一个长度为N且不和其他相同的子串。
但是因为复杂度仍然nlgn,所以也会超时。
代码如下:
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
template<class T>
inline bool read(T &n){
T x = 0, tmp = 1; char c = getchar();
while ((c < '0' || c > '9') && c != '-' && c != EOF) c = getchar();
if (c == EOF) return false;
if (c == '-') c = getchar(), tmp = -1;
while (c >= '0' && c <= '9') x *= 10, x += (c - '0'), c = getchar();
n = x*tmp;
return true;
}
template <class T>
inline void write(T n) {
if (n < 0) {
putchar('-');
n = -n;
}
int len = 0, data[20];
while (n) {
data[len++] = n % 10;
n /= 10;
}
if (!len) data[len++] = 0;
while (len--) putchar(data[len] + 48);
}
const int MAX =16000000;
int N,NC;
int m[256];
char a[MAX];
int sa[MAX];
int str[MAX];
int height[MAX];
void radix(int * str, int *a, int *b, int n, int m){
static int count[MAX];
memset(count,0,sizeof(count));
for(int i = 0; i < n; ++i) ++count[str[a[i]]];
for(int i = 1; i <= m; ++i) count[i] += count[i-1];
for(int i = n -1; i >= 0; --i) b[--count[str[a[i]]]] = a[i];
}
void suffix_array(int* str,int * sa, int n, int m)
{
static int rank[MAX],a[MAX],b[MAX];
for(int i = 0; i < n; ++i) rank[i] =i;
radix(str,rank,sa,n,m);
rank[sa[0]] = 0;
for(int i = 1; i < n; ++i)
rank[sa[i]]= rank[sa[i-1]] +(str[sa[i]]!=str[sa[i-1]]);
for(int i = 0; 1<<i< n; ++i){
for(int j = 0; j < n; ++j){
a[j] = rank[j]+1;
b[j] = j + (1<<i) >=n? 0: rank[j + (1<<i)] + 1;
sa[j] = j;
}
radix(b,sa,rank,n,n);
radix(a,rank,sa,n,n);
rank[sa[0]] = 0;
for(int j = 1; j < n; ++j){
rank[sa[j]] = rank[sa[j-1]] + (a[sa[j-1]] != a[sa[j]] || b[sa[j-1]] != b[sa[j]]);
}
}
}
void calc_height(int * str, int * sa, int * height, int n)
{
static int rank[MAX];
for(int i = 0 ; i < n; ++i) rank[sa[i]] = i;
int h = 0;
for(int i = 0; i < n; ++i){
h = h == 0?0: h - 1;
if(rank[i]!= 0)
while(str[i + h] == str[sa[rank[i]-1] + h]) h++;
height[rank[i]] = h;
}
}
int main(void)
{
//freopen("input.txt","r",stdin);
int T;
scanf("%d", &T);
int cas = 0;
while(T--){
scanf("%d %d", &N,&NC);
scanf("%s",a);
int n = strlen(a);
copy(a,a+n,str);
suffix_array(str,sa,n,256);
calc_height(str,sa,height,n);
int ans = 0;
for(int i = 0; i < n; ++i){
if(height[i] < N && sa[i] + N - 1 < n)
ans++;
//printf("%d %d %d\n",ans,height[i],sa[i]);
}
if(cas++ != 0)
puts("");
printf("%d\n",ans);
}
return 0;
}