POJ 1200 Crazy search

题意:给出一串包含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;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值