zoukankan      html  css  js  c++  java
  • Substring (后缀数组 + 计数)

    题意:求出字符串中包含了某个字符的字符序列不一样的数量。

    思路:其实主要的是找出每个被包含字符的数量,假设除了目标字符之外的所有字符都不一样,那么应该就很好求了,但是显然不可能,所以我们可以枚举每一个起点,个数应该是从他的下一个字符是目标字符起的所有数量,但是通过观察我们可以发现这样计算我们又会多计算了一部分,例如a , abbabbabb 在计算第四个和第七个时,我们会多计算了a, ab, abb 或者计算第二位和第五位时多计算了bba,bbab,bbabb,我们可以这是就是相当于后缀数组里面的height数组。

    #include<bits/stdc++.h>
    using namespace std;
    
    typedef long long ll;
    const int MAXN = 200100;
    bool cmp(int *r, int a, int b, int l) {
        return r[a] == r[b] && r[a + l] == r[b + l];
    }
    int t1[MAXN], t2[MAXN], c[MAXN];
    
    void da(int str[], int sa[], int rk[], int height[], int n, int m) {
        n++;
        int i, j, p, *x = t1, *y = t2;
    //第一轮基数排序,如果 s 的最大值很大,可改为快速排序
        for(i = 0; i < m; i++)
            c[i] = 0;
        for(i = 0; i < n; i++)
            c[x[i] = str[i]]++;
        for(i = 1; i < m; i++)
            c[i] += c[i - 1];
        for(i = n - 1; i >= 0; i -- )
            sa[ -- c[x[i]]] = i;
        for(j = 1; j <= n; j <<= 1) {
            p = 0;
    //直接利用 sa 数组排序第二关键字
            for(i = n - j; i < n; i++)
                y[p++] = i;//后面的 j 个数第二关键字为空的最小
            for(i = 0; i < n; i++)
                if(sa[i] >= j)
                    y[p++] = sa[i] - j;
    //这样数组 y 保存的就是按照第二关键字排序的结果
    //基数排序第一关键字
            for(i = 0; i < m; i++)
                c[i] = 0;
            for(i = 0; i < n; i++)
                c[x[y[i]]]++;
            for(i = 1; i < m; i++)
                c[i] += c[i - 1];
            for(i = n - 1; i >= 0; i -- )
                sa[ -- c[x[y[i]]]] = y[i];
    //根据 sa 和 x 数组计算新的 x 数组
            swap(x, y);
            p = 1;
            x[sa[0]] = 0;
            for(i = 1; i < n; i++)
                x[sa[i]] = cmp(y, sa[i - 1], sa[i], j) ? p - 1 : p++;
            if(p >= n)
                break;
            m = p;//下次基数排序的最大值
        }
        int k = 0;
        n -- ;
        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(str[i + k] == str[j + k])
                k++;
            height[rk[i]] = k;
        }
    }
    int rk[MAXN], height[MAXN], sa[MAXN], in[MAXN];
    char s[MAXN], t[MAXN];
    vector<int>vec;
    vector<int>:: iterator it;
    
    int main() {
        int T;scanf("%d", &T);
        for(int ncase = 1; ncase <= T; ncase ++){
            scanf("%s%s",t, s);
            int n = strlen(s);
            vec.clear();
            for(int i = 0; i < n; i ++){
                in[i] = s[i];
                if(s[i] == t[0]) vec.push_back(i);
            }in[n] = 0;
            da(in, sa, rk, height, n, 128);
            ll ans = 0;
            for(int i = 1; i <= n; i ++){
                it = lower_bound(vec.begin(), vec.end(), sa[i]);
                printf("%d %d %d
    ", sa[i], *it, height[i]);
                if(it == vec.end()) continue;
                int tmp = n - sa[i] - max(height[i], *it - sa[i]);
                if(tmp > 0) ans += tmp;
            printf("Case #%d: %lld
    ",ncase, ans);
            }printf("
    ");
        }
        return 0;
    }
    more crazy more get!
  • 相关阅读:
    通讯录封装实现
    简单通讯录的实现 main..h .m文件全部
    iOS 开发 OC编程 字典和集合 排序方法
    iOS 开发 OC编程 数组冒泡排序.图书管理
    iOS 开发 OC编程 属性和字符串练习
    iOS 开发 OC编程 属性和字符串
    iOS 开发 OC编程 便利构造器以及初始化方法
    iOS 开发 OC编程 方法的书写
    IOS 开发 OC编程 类和对象
    iOS 开发 c语言阶段考试题
  • 原文地址:https://www.cnblogs.com/wethura/p/9864186.html
Copyright © 2011-2022 走看看