HDOJ 5769 Substring

题意

求一个字符串有多少个不同的子串,要求这些子串都包含至少一次x字符。

思路

如果不考虑包含x字符的话,求不同子串个数是一个后缀数组经典问题(spoj 694)。考虑那个问题的做法: leni=1lensa[i]lcp[i] ,len-sa[i]就是sa[i]开头的串的个数,再减去sa[i-1]时减过的lcp[i],累加起来就是答案了。然后这题直接只需要记录一下对每个下标i的后面最近出现x的位置nxt[i]是多少。然后在计算子串个数的时候不要计算nxt[i]之前的串就可以了。
我用之前那个题的代码改了一下结果TLE了。后来发现是板子的问题,挑战程序设计竞赛的板子使用的快排(出题人竟然卡这个。。话说这个板子A了很多题了这是第一次被卡= =
然后换成大白书上的过了。

代码

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
#define LL long long
#define Lowbit(x) ((x)&(-x))
#define lson l, mid, rt << 1
#define rson mid + 1, r, rt << 1|1
#define MP(a, b) make_pair(a, b)
const int INF = 0x3f3f3f3f;
const int MOD = 1000000007;
const int maxn = 1e5 + 10;
const double eps = 1e-8;
const double PI = acos(-1.0);
typedef pair<int, int> pii;


//int temp[maxn], lcp[maxn];

char x[2];
char str[maxn];
int nxt[maxn];

int sa[maxn], rk[maxn], height[maxn];
int c[maxn], t[2][maxn], s[maxn];

void build(int n, int m) {
    int i, k, p, *x = t[0], *y = t[1];
    memset(c, 0, m << 2);
    for(i = 0; i < n; ++i) ++c[x[i] = s[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(k = 1; k <= n; k <<= 1) {
        p = 0;
        for(i = n - k; i < n; ++i) y[p++] = i;
        for(i = 0; i < n; ++i) if(sa[i] >= k) y[p++] = sa[i] - k;
        memset(c, 0, m << 2);
        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];
        swap(x, y); p = 1; x[sa[0]] = 0;
        for(i = 1; i < n; ++i)
            x[sa[i]] = (y[sa[i - 1]] == y[sa[i]] &&
                        y[sa[i - 1] + k] == y[sa[i] + k]) ? p - 1 : p++;
        if(p >= n) break; m = p;
    }
    for(i = 0; i < n; ++i) rk[sa[i]] = i;
    for(i = 0, k = 0; i < n; ++i) {
        if(k) k--;
        if(!rk[i]) continue;
        int j = sa[rk[i] - 1];
        while(s[i + k] == s[j + k]) k++;
        height[rk[i]] = k;
    }
}

//int compare_sa(int i, int j)
//{
//    if (rk[i] != rk[j]) return rk[i] < rk[j];
//    else
//    {
//        int ri = i + k <= n ? rk[i+k] : -1;
//        int rj = j + k <= n ? rk[j+k] : -1;
//        return ri < rj;
//    }
//}
//
//void construct_sa(char *s, int *sa)
//{
//    for (int i = 0; i <= n; i++)
//    {
//        sa[i] = i;
//        rk[i] = i < n ? s[i] : -1;
//    }
//    for (k = 1; k <= n; k *= 2)
//    {
//        sort(sa, sa + n + 1, compare_sa);
//        temp[s[0]] = 0;
//        for (int i = 1; i <= n; i++)
//            temp[sa[i]] = temp[sa[i-1]] + (compare_sa(sa[i-1], sa[i]) ? 1 : 0);
//        for (int i = 0; i <= n; i++)
//            rk[i] = temp[i];
//    }
//}
//
//void construct_lcp(char *s, int *sa, int *lcp)
//{
//    for (int i = 0; i <= n; i++) rk[sa[i]] = i;
//    int h = 0;
//    lcp[0] = 0;
//    for (int i = 0; i < n; i++)
//    {
//        int j = sa[rk[i] - 1];
//        if (h > 0) h--;
//        for (; j + h < n && i + h < n; h++)
//            if (s[j+h] != s[i+h]) break;
//        lcp[rk[i] - 1] = h;
//    }
//}



int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int T;
    scanf("%d", &T);
    for (int ncase = 1; ncase <= T; ncase++)
    {
        scanf("%s%s", x, str);
        int n = strlen(str);
        for (int i = 0; i < n; i++) s[i] = str[i] - 'a' + 1;
        s[n] = 0;
        build(n + 1, 30);
        nxt[n] = n;
        for (int i = n - 1; i >= 0; i--)
        {
            nxt[i] = nxt[i+1];
            if (str[i] == x[0]) nxt[i] = i;
        }
        LL ans = 0;
        for (int i = 1; i <= n; i++)
        {
            //printf("%d %d %d %d\n", i, sa[i], height[i], nxt[sa[i]]);
            ans += n - max(sa[i] + height[i], nxt[sa[i]]);
        }
        printf("Case #%d: %lld\n", ncase, ans);
    }
    return 0;
}

你可能感兴趣的:(HDOJ,后缀数组)