2016多校联合训练4 F - Substring 后缀数组

Description

?? is practicing his program skill, and now he is given a string, he has to calculate the total number of its distinct substrings. 
But ?? thinks that is too easy, he wants to make this problem more interesting. 
?? likes a character X very much, so he wants to know the number of distinct substrings which contains at least one X. 
However, ?? is unable to solve it, please help him.

Input

The first line of the input gives the number of test cases T;T test cases follow. 
Each test case is consist of 2 lines: 
First line is a character X, and second line is a string S. 
X is a lowercase letter, and S contains lowercase letters(‘a’-‘z’) only.

T<=30 
1<=|S|<=10^5 
The sum of |S| in all the test cases is no more than 700,000.

Output

For each test case, output one line containing “Case #x: y”(without quotes), where x is the test case number(starting from 1) and y is the answer you get for that case.

Sample Input

2
a
abc
b
bbb

Sample Output

Case #1: 3
Case #2: 3

Hint

In first case, all distinct substrings containing at least one a: a, ab, abc. In second case, all distinct substrings containing at least one b: b, bb, bbb.

思路:题目是给出一个串,求出这个串有多少个包含字符x的不同子串。首先考虑总的可能的方案数,对于每一个位置i,求出在它右边离她最近的字符x的位置p,那么这个位置贡献的答案就是n-p; 这样算出来的方案数显然会有重复,考虑去重。 我们对串求一遍后缀数组得到heigh数组,枚举heigh数组的每一个元素heigh[i],

即heigh[i] = LCP(sa[i-1],sa[i]);  如果在lcp这一段字符包含了x,且位置为pos,那么说明有lcp-pos个串重复了

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <cmath>
#include <vector>
#include <bitset>
#define ll long long
#define rep(x,to) for(int x=0;x<(to);x++)
#define repn(x,to) for(int x=1;x<=(to);x++)
#define clr(x) memset(x,0,sizeof(x))
#define pli pair<ll, int>
#define MEMSET(x,v) memset(x,v,sizeof(x))
#define pll pair<ll,ll>
#define pb push_back
#define MP make_pair
using namespace std;
const int N = 5e5 + 100;
int t1[N], t2[N], c[N];

bool cmp(int *r, int a, int b, int l) {
    return r[a] == r[b] && r[a+l] == r[b+l];
}
void da(int str[], int sa[], int Rank[], int heigh[], int n, int m) {
    n++;
    int i, j, p, *x = t1, *y = t2;
    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;
        for(i = n - j; i < n; ++i) y[p++] = i;
        for(i = 0; i < n; ++i) if(sa[i] >= j) y[p++] = sa[i] - j;

        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];
        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) Rank[ sa[i] ] = i;
    for(i = 0; i < n; ++i) {
        if(k) k--;
        j = sa[ Rank[i] - 1 ];
        while(str[i + k] == str[j + k]) k++;
        heigh[ Rank[i] ] = k;
    }
}

int Rank[N], heigh[N];
char str[N];
int r[N];
int sa[N];
char X[12];
int pre[N];
int pos[N];
int _ = 1;

void solve(int n) {
    memset(pos, 0, sizeof pos);
    int p = -1;
    ll sum = 0, num = 0;
    for(int i = n - 1; i >= 0; --i) {
        if(str[i] == X[0]) p = i;
        pos[i] = p;
        if(pos[i] != -1) sum += (n - pos[i]);
    }
    //for(int i = 0; i < n; ++i) printf("%d ", pos[i]);

    for(int i = 2; i <= n; ++i) {

        if(pos[ sa[i - 1] ] == -1 || pos[ sa[i] ] == -1) continue;
        int c1 = pos[ sa[i - 1] ] - sa[i - 1] + 1;
        int c2 = pos[ sa[i] ] - sa[i] + 1;
        int h = heigh[i];
        if(c1 > h || c2 > h) continue;
        num += (h - c1 + 1);
    }
    printf("Case #%d: %I64d\n", _++, sum - num);
}
int main()
{
#ifdef LOCAL
    freopen("in", "r", stdin);
#endif
    int cas; scanf("%d", &cas);
    while(cas --) {
        scanf("%s%s", X, str);
        int n = strlen(str);
        for(int i = 0; i < n; ++i) r[i] = str[i];
        r[n] = 0;
        da(r, sa, Rank, heigh, n, 128);

        solve(n);
       // for(int i = 0; i < n; ++i) printf("%d ", Rank[i]); puts("");
       // for(int i = 1; i <= n; ++i) printf("%d ", sa[i]); puts("");
       // for(int i = 2; i <= n; ++i) printf("%d ", heigh[i]); puts("");
    }
    return 0;
}

时间: 2024-10-08 03:29:57

2016多校联合训练4 F - Substring 后缀数组的相关文章

河南多校联合训练 F 不是匹配

描述 有N个人,N个活动, 每个人只会对2个或者3个活动感兴趣, 每个活动也只有两个人或者两个活动对它兴趣,每个人参加一个 感兴趣的活动需要一天 ,且当天该活动被参加时,其他的人不能参加 如果每个人都参加完自己有兴趣的活动,应当怎样安排使得所用总天数时间最短 2<= N <=1000, 1<=m<=1000; 输入 一个数T 表示T 组数据每组一个N表示人数,编号1 -- N , 一个数 m ,接下来m 行每个两个数x,y, 表示第 x 个人对第y个活动感兴趣 输出 每组输出一个整

2016多校联合训练1 B题Chess (博弈论 SG函数)

题目大意:一个n(n<=1000)行,20列的棋盘上有一些棋子,两个人下棋,每回合可以把任意一个棋子向右移动到这一行的离这个棋子最近的空格上(注意这里不一定是移动最后一个棋子),不能移动到棋盘外,不能移动了就算输,两个人都用最优策略,问先手是否有必胜策略. 这题显然就是SG函数了吧.行与行之间互不影响,所以可以看成n个子游戏,求出它们各自的SG函数然后异或一下就可以了.我们发现只有20列,2^21=2097152,所以我们可以先把行的所有情况的SG函数预处理出来,然后每次询问O(1)就行了. 代

HDU 5371 (2015多校联合训练赛第七场1003)Hotaru&#39;s problem(manacher+二分/枚举)

HDU 5371 题意: 定义一个序列为N序列:这个序列按分作三部分,第一部分与第三部分相同,第一部分与第二部分对称. 现在给你一个长为n(n<10^5)的序列,求出该序列中N序列的最大长度. 思路: 来自官方题解:修正了一些题解错别字(误 先用求回文串的Manacher算法,求出以第i个点为中心的回文串长度,记录到数组p中 要满足题目所要求的内容,需要使得两个相邻的回文串,共享中间的一部分,也就是说,左边的回文串长度的一半,要大于等于共享部分的长度,右边回文串也是一样. 因为我们已经记录下来以

HDU 5371 (2015多校联合训练赛第七场1003)Hotaru&amp;#39;s problem(manacher+二分/枚举)

pid=5371">HDU 5371 题意: 定义一个序列为N序列:这个序列按分作三部分,第一部分与第三部分同样,第一部分与第二部分对称. 如今给你一个长为n(n<10^5)的序列,求出该序列中N序列的最大长度. 思路: 来自官方题解:修正了一些题解错别字(误 先用求回文串的Manacher算法.求出以第i个点为中心的回文串长度.记录到数组p中 要满足题目所要求的内容.须要使得两个相邻的回文串,共享中间的一部分,也就是说.左边的回文串长度的一半,要大于等于共享部分的长度,右边回文串也

2014多校联合训练第一场(组队训练)

这是我.potaty.lmz第二次训练,毕竟经验不足,加上水平不够,导致我们各种被碾压. A - Couple doubi: 这道题是道比较水的数论.但我们都没想出来要怎么做.后来是potaty提议打个表看看,然后lmz打出表后发现了规律.我还没细看,待研究后再补全. D - Task: 这道题一看就知道是个贪心(现在只要是有deadline的题我都觉得是贪心了).虽然想出来了,但还是不会严格证明为什么只要取满足task的且y最小(y相等时x最小)的machine就行了. 我的做法是把所有mac

hdu_1403_Longest Common Substring(后缀数组的应用)

题目链接:hdu_1403_Longest Common Substring 题意: 给你两个字符串,然你找最长的公共子串 题解: 后缀数组的经典应用,要找两个字符串的公共子串,那么就相当于找两个串的后缀的最长公共前缀,我们将两个字符串拼接在一起,中间插一个特殊字符 然后我们考虑height数组,height数组存的是排i和i-1的最长前缀,如果sa[i]和sa[i-1]在特殊字符的两边,那么这个height[i]记录的就是这两个串的最长 子串,然后扫一遍height数组更新一下答案就行了 1

poj 3693 Maximum repetition substring(后缀数组)

题目链接:poj 3693 Maximum repetition substring 题目大意:求一个字符串中循环子串次数最多的子串. 解题思路:对字符串构建后缀数组,然后枚举循环长度,分区间确定.对于一个长度l,每次求出i和i+l的LCP,那么以i为起点,循环子串长度为l的子串的循环次数为LCP/l+1,然后再考虑一下从i-l+1~i之间有没有存在增长的可能性. #include <cstdio> #include <cstring> #include <vector>

hdu 5769 Substring 后缀数组 + KMP

http://acm.hdu.edu.cn/showproblem.php?pid=5769 题意:在S串中找出X串出现的不同子串的数目? 其中1 官方题解: 处理出后缀数组中的sa[]数组和height[]数组.在不考虑包含字符X的情况下,不同子串的个数为 如果要求字符X,只需要记录距离sa[i]最近的字符X的位置(用nxt[sa[i]]表示)即可,个数 理解:后缀数组height[i]就是sa[i]与sa[i-1]的LCP,在后缀数组中求解全部的不同子串(之前只写过SAM处理所有不同子串..

POJ 3693 Maximum repetition substring ——后缀数组

重复次数最多的字串,我们可以枚举循环节的长度. 然后正反两次LCP,然后发现如果长度%L有剩余的情况时,答案是在一个区间内的. 所以需要找到区间内最小的rk值. 两个后缀数组,四个ST表,$\Theta(n\log n)$ 就可以解决了 空间卡死了,瞎晶胞卡过去了. #include <map> #include <cmath> #include <queue> #include <cstdio> #include <cstring> #incl