1. 程式人生 > >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
#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)

題目大意:給你一個字串,給你N次查詢,每次給你一個區間讓你求出這個區間裡面有多少子串。 解題思路:我們肯定要列舉位置,然後找公共子串然後再去掉重複的,但是他的地址對應的rank不是連續的,如果暴力找的話會n*n會超時。 從這個部落格學習到一種方法:首先對整個字串求一次sa[

HDU 4622 Reincarnation區間不相同個數:字串雜湊 | 字尾陣列 | 字尾自動機)

Problem Description Now you are back,and have a task to do: Given you a string s consist of

1166不相同的的個數——字尾陣列

題目描述 給定一個字串,求不相同的子串的個數。 輸入 輸入資料第一行為一個數字 T,表示資料組數。(T<=10) 接下來的 T 行,每行一個由小寫或大寫字母構成的字串,字串長度不超過 50000。 輸出 對於每組資料,輸出一行一個數字,表示答案。 樣例輸入 4

SPOJ-694-求字串中不同個數(字尾陣列

http://www.spoj.com/status/ns=17418952 【每一個子串必然是某個字尾的字首】,因此我們統計出所有的字尾中有多少個不同的字首,就是所有不重複子串的數量了 而這個相同的字首個數,當然就是所有height之和啦。 所以答案就是n*(n-1)/

能被 K 整除的最大連續長度

images 都是 str image ace 數組a 暴力 輸出 這一 【來源】網上流傳的2017美團秋招筆試題 【問題描述】 兩個測試樣例輸出都是5 【算法思路】 暴力解法時間會超限,使用一種很巧妙的數學方法。用在讀取數組arr時用數組sum記錄其前 i 項的和,

POJ 3261 Milk Patterns ( 後綴數組 && 出現k次最長可重疊長度 )

b+ 重疊 可能性 nbsp 如果 ide sca 技術 print 題意 : 給出一個長度為 N 的序列,再給出一個 K 要求求出出現了至少 K 次的最長可重疊子串的長度 分析 : 後綴數組套路題,思路是二分長度再對於每一個長度進行判斷,判斷過程就是對於 Height

動態規劃問題二:最長重複/公共長度求解

問題一:求串s1,s2最長公共子串長度 問題二:求串s的最長重複子串 問題三:求串s1,s2最長公共子序列長度 注:兩個字串的最長公共子序列與最長公共子串區別是前者字元之間不一定是連在一塊的,而公共子串必須要連在一塊。比如字串1:ABCAB;字串2:ADEBCFA,則這兩個字串的最長公共子序

hdu 4622 Reincarnation 字尾自動機記憶化

題目要求處理出詢問的從i到j的子串的不同子串的數量,因為串長最大2000,所以n^2+記憶化處理出任意從i到j的不同子串數量(即每個節點的max-其父節點的max) #include<bits/stdc++.h> #include<iostream&g

[LEETCODE]#3 Longest Substring Without Repeating Characters 最長不重複的長度, Sliding Window, 滑動視窗

https://leetcode.com/problems/longest-substring-without-repeating-characters/   Given a string, find the length of the longest substring

poj 2774 字尾陣列 求最長連續公共長度

Long Long Message Time Limit: 4000MS Memory Limit: 131072K Total Submissions: 35480 Accepted: 14218 Case Time Limit: 10

求字元最長且不重複的長度(暴力以及滑動視窗解決)

題目:給定一個字串,找出不含有重複字元的最長子串的長度。示例:給定 "abcabcbb" ,沒有重複字元的最長子串是 "abc" ,那麼長度就是3。給定 "bbbbb" ,最長的子串就是 "b" ,長度是1。給定 "pwwkew" ,最長子串是 "wke" ,長度是3。請注意

Longest Consecutive Sequence:無序數列中查詢最長連續長度

Given an unsorted array of integers, find the length of the longest consecutive elements sequence.Your algorithm should run in O(n) comple

一個字串中包含另一個字串所有字元的最短長度?——《程式設計之美》最短摘要的生成的簡化

題目: 給定一個字串及一個字串集合A,求該字串中包含A中所有字元的最短子串長度。 解決方案一: 最直接的方法就是,直接開始遍歷:查詢任意兩個子串之間是否包含str2,如果包含,記錄下長度,求得最小值即可。 str1 = "daebfacba"; str2 = "abc";

O(n)的方法求最長迴文長度(Manacher演算法)

大體思路其實就是找出一箇中心點,判斷中心點兩端的迴文串半徑是多少; 但由於找中心點的方法只適用於奇數長的迴文串,故可以在每兩個字元間插入一個間隔符來幫助結算; 用rd[i]表示以經過填充後的字串裡的第i個字元為中心,它的迴文串長度; 可以得知,在【

字符——最長公共(長度)

comm subst 子串 charat ati println out style print 1 public static int longestCommonSubstring(String s1, String s2) { 2 in

計算字符長度加車頭

win 文本 字符串長度 color red str ase length function 計算字符串長度,加車頭.切換到IE模式下使用 0 // 計算字符串長度,加車頭

javaScript中自定義sort中的比較函數用於比較字符長度數值大小

var cti lin family 字符串長度 tr1 個數 new fun var arr = [‘aa‘, 23, 1.2, ‘bb‘, ‘cc‘]; var len = arr.length; // for (var i = 0; i < len; i++)

【京東】2016研發工程師(第一題沒做出來動態規劃類似公共重點反覆看;第三題沒有做出來自我檢討)

[程式設計題]年終獎 小東所在公司要發年終獎,而小東恰好獲得了最高福利,他要在公司年會上參與一個抽獎遊戲,遊戲在一個6*6的棋盤上進行,上面放著36個價值不等的禮物,每個小的棋盤上面放置著一個禮物,他需要從左上角開始遊戲,每次只能向下或者向右移動一步,到達右下角停止,一路上的格子裡的禮物小東都能

POJ-1743 Musical Theme(最長不可重疊字尾陣列+二分)

A musical melody is represented as a sequence of N (1<=N<=20000)notes that are integers in the range 1..88, each representing a key on the piano. I

UVA-11107 Life Forms(求出現K次的字尾陣列+二分答案)

題解: 題意: 輸入n個DNA序列,你的任務是求出一個長度最大的字串,使得它在超過一半的DNA序列中出現。如果有多解,按照字典序從小到大輸入所有解。 把n個DNA序列拼在一起,中間用沒有出現過的字元分割。然後求出height陣列。 二分滿足要求的字串長度L,然後判斷是否可行。 判斷可行: 分組