首页 > 代码库 > HDU 4622 Reincarnation( 任意区间子串的长度, 后缀数组+RMQ)

HDU 4622 Reincarnation( 任意区间子串的长度, 后缀数组+RMQ)

题目大意:给你一个字符串,给你N次查询,每次给你一个区间让你求出这个区间里面有多少子串。

解题思路:我们肯定要枚举位置,然后找公共子串然后再去掉重复的,但是他的地址对应的rank不是连续的,如果暴力找的话会n*n会超时。

从这个博客学习到一种方法:首先对整个字符串求一次sa[]以及height[],之后对于任意区间[L, R],遍历一遍sa[],只要起点在[L, R]内的后缀就需要进行统计,类似于1)中的方法,不过有一个地方要特别注意的就是全部的sa[]不一定就是区间内的sa[],这是因为区间内的后缀比较时有额外的长度限制。可以证明遍历的过程要遵循如下的规则:


后缀s1和后缀s2现在是两个待比较的后缀,s1在前,s2在后,其起点都在区间[L, R]内,并设两串在区间中的长度为 len1, len2, 其全局的最长公共前缀为 lcp。现考虑在遍历sa[]时,如何从全局sa[]得到正确的局部sa[]:
1: lcp < len1 && lcp < len2 时说明两个串在未结束时就比较出了大小,全局和局部的sa[]统一,因此可以放心令s2作为下一个字典序后缀;
2: lcp >= len1 && lcp >= len2 时说明在其中一个串结束时,两个串对应字符都是相等的,这时需要根据len1和len2关系来决定,如果len1>len2那么就不用更换了;
3: lcp >= len1 && lcp < len2 时说明在其中一个串结束时,两个串对应字符都是相等的,由于s2的长度比s1长,因此字典序肯定大,因此需要更换当前的后缀;
4: lcp < len1 && lcp >= len2 时说明在其中一个串结束时,两个串对应字符都是相等的,由于s1的长度比s2长,因此字典序肯定大,因此不需更换当前的后缀。
其中2和4条件可以合并,如果4成立,那么必定有 len1 > len2,因此可以简化这个判断这个过程:if (len1 > len2 && lcp >= len2) 则不更换 else 更换。
 if(la > lb && lcp >= lb){不交换} else {交换};

直接理解就是给结果带来误差的情况只会是某个被lcp完全包含的后缀被排在了后面,那么它的正确位置应该是在最前面,由于在后面匹配的其长度仍未整个局部后缀的长度,而在选择下一个字典序后缀时屏蔽掉这个后缀即可。

Reincarnation

Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 131072/65536 K (Java/Others)
Total Submission(s): 2277    Accepted Submission(s): 795


Problem Description
Now you are back,and have a task to do:
Given you a string s consist of lower-case English letters only,denote f(s) as the number of distinct sub-string of s.
And you have some query,each time you should calculate f(s[l...r]), s[l...r] means the sub-string of s start from l end at r.
 

Input
The first line contains integer T(1<=T<=5), denote the number of the test cases.
For each test cases,the first line contains a string s(1 <= length of s <= 2000).
Denote the length of s by n.
The second line contains an integer Q(1 <= Q <= 10000),denote the number of queries.
Then Q lines follows,each lines contains two integer l, r(1 <= l <= r <= n), denote a query.
 

Output
For each test cases,for each query,print the answer in one line.
 

Sample Input
2 bbaba 5 3 4 2 2 2 5 2 4 1 4 baaba 5 3 3 3 4 1 4 3 5 5 5
 

Sample Output
3 1 7 5 8 1 3 8 5 1
Hint
I won‘t do anything against hash because I am nice.Of course this problem has a solution that don‘t rely on hash.
 

Source
2013 Multi-University Training Contest 3
 

#include <algorithm>
#include <iostream>
#include <stdlib.h>
#include <string.h>
#include <iomanip>
#include <stdio.h>
#include <string>
#include <queue>
#include <cmath>
#include <stack>
#include <ctime>
#include <map>
#include <set>
#define eps 1e-9
///#define M 1000100
///#define LL __int64
#define LL long long
///#define INF 0x7ffffff
#define INF 0x3f3f3f3f
#define PI 3.1415926535898
#define zero(x) ((fabs(x)<eps)?0:x)
#define mod 1000000007

#define Read() freopen("autocomplete.in","r",stdin)
#define Write() freopen("autocomplete.out","w",stdout)
#define Cin() ios::sync_with_stdio(false)

using namespace std;


inline int read()
{
    char ch;
    bool flag = false;
    int a = 0;
    while(!((((ch = getchar()) >= '0') && (ch <= '9')) || (ch == '-')));
    if(ch != '-')
    {
        a *= 10;
        a += ch - '0';
    }
    else
    {
        flag = true;
    }
    while(((ch = getchar()) >= '0') && (ch <= '9'))
    {
        a *= 10;
        a += ch - '0';
    }
    if(flag)
    {
        a = -a;
    }
    return a;
}
void write(int a)
{
    if(a < 0)
    {
        putchar('-');
        a = -a;
    }
    if(a >= 10)
    {
        write(a / 10);
    }
    putchar(a % 10 + '0');
}

const int maxn = 2050;

int wa[maxn], wb[maxn], wv[maxn], ws1[maxn];
int sa[maxn];

int cmp(int *r, int a, int b, int l)
{
    return r[a] == r[b] && r[a+l] == r[b+l];
}

void da(int *r, int *sa, int n, int m)
{
    int i, j, p, *x = wa, *y = wb;
    for(i = 0; i < m; i++) ws1[i] = 0;
    for(i = 0; i < n; i++) ws1[x[i] = r[i]]++;
    for(i = 1; i < m; i++) ws1[i] += ws1[i-1];
    for(i = n-1; i >= 0; i--) sa[--ws1[x[i]]] = i;

    for(j = 1, p = 1; p < n; j <<= 1, m = p)
    {
        for(p = 0, 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 < n; i++) wv[i] = x[y[i]];
        for(i = 0; i < m; i++) ws1[i] = 0;
        for(i = 0; i < n; i++) ws1[wv[i]]++;
        for(i = 1; i < m; i++) ws1[i] += ws1[i-1];
        for(i = n-1; i >= 0; i--) sa[--ws1[wv[i]]] = y[i];
        for(swap(x, y), p = 1, x[sa[0]] = 0, i = 1; i < n; i++)
            x[sa[i]] = cmp(y, sa[i-1], sa[i], j)?p-1:p++;
    }
}


int rank[maxn], height[maxn];
void calheight(int *r, int *sa, int n)
{
    int i, j, k = 0;
    for(i = 1; i <= n; i++) rank[sa[i]] = i;
    for(int i = 0; i < n; height[rank[i++]] = k)
        for(k?k--:0, j = sa[rank[i]-1]; r[i+k] == r[j+k]; k++);
    return ;
}

int dp[maxn][30];


void RMQ(int len)
{
    for(int i = 1; i <= len; i++)
        dp[i][0] = height[i];
    for(int j = 1; 1<<j <= maxn; j++)
    {
        for(int i = 1; i+(1<<j)-1 <= len; i++)
            dp[i][j] = min(dp[i][j-1], dp[i+(1<<(j-1))][j-1]);
    }
}
int lg[maxn];

int querry(int l, int r)
{
    int k = lg[r-l+1];
    return min(dp[l][k], dp[r-(1<<k)+1][k]);
}


void init()
{
    lg[0] = -1;
    for (int i = 1; i < maxn; ++i)
        lg[i] = lg[i>>1] + 1;
}
char str[maxn];
int seq[maxn];


int Del(int l, int r, int n)
{
    int ans = (r-l+2)*(r-l+1)/2;
    int last = -1;
    int k = r-l+1;
    for(int i = 1; i <= n; i++)
    {
        if(!k) break;
        if(sa[i] < l || sa[i] > r) continue;
        k--;
        if(last == -1)
        {
            last = i;
            continue;
        }
        int a = last;
        int b = i;
        if(a > b) swap(a, b);
        int lcp = querry(a+1, b);
        int la = r-sa[last]+1;
        int lb = r-sa[i]+1;
        if(la > lb && lcp >= lb){}
        else last = i;
        ans -= min(lcp, min(la, lb));
    }
    return ans;
}

int main()
{
    int T;
    cin >>T;
    init();
    while(T--)
    {
        scanf("%s", str);
        int len = strlen(str);
        for(int i = 0; i < len; i++)
            seq[i] = str[i]-'a'+1;
        seq[len] = 0;
        da(seq, sa, len+1, 27);
        calheight(seq, sa, len);
        RMQ(len);
        int n;
        int l, r;
        scanf("%d",&n);
        while(n--)
        {
            scanf("%d %d",&l, &r);
            printf("%d\n", Del(l-1, r-1, len));
        }
    }
    return 0;
}



HDU 4622 Reincarnation( 任意区间子串的长度, 后缀数组+RMQ)