HDU 4622 Reincarnation( 任意區間子串的長度, 字尾陣列+RMQ)

畫船聽雨發表於2015-01-23

題目大意:給你一個字串,給你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
 

#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;
}



相關文章