POJ - 3415 Common Substrings(后缀数组求长度不小于 k 的公共子串的个数+单调栈优化)

Description

A substring of a string T is defined as:

Tik)= TiTi+1... Ti+k-1, 1≤ i≤ i+k-1≤| T|.

Given two strings AB and one integer K, we define S, a set of triples (ijk):

S = {( ijk) | k≥ KAik)= Bjk)}.

You are to give the value of |S| for specific AB and K.

Input

The input file contains several blocks of data. For each block, the first line contains one integer K, followed by two lines containing strings A and B, respectively. The input file is ended by K=0.

1 ≤ |A|, |B| ≤ 105

1 ≤ K ≤ min{|A|, |B|}

Characters of A and B are all Latin letters.

Output

For each case, output an integer |S|.

Sample Input

2
aababaa
abaabaa
1
xx
xx
0

题意:求长度不小于 k 的公共子串的个数。

思路:还是论文上的题目。基本思路是计算 A 的所有后缀和 B 的所有后缀之间的最长公共前缀的长度。把最长公共前缀长度不小于 k 的部分所有加起来。先将两个字符串连起来,中间用一个没有出现过的字符隔开。按 height 值分组后,接下来的工作便是高速的统计每组中后缀之间的最长公共前缀之和。扫描一遍,每遇到一个 B 的后缀就统计与前面的 A 的后缀能产生多少个长度不小于 k 的公共子串,这里 A 的后缀须要用一个单调的栈来高效的维护。然后对 A 也这样做一次。

比較难理解的是单调栈这部分。还是通过举例来说吧,如果当前的height[]数组按排名的顺序依次是:1,2,3.如果这些都大于等于k值,且sa[0],sa[1],sa[2]都是B串的,当sa[3]是A串的时候,由于它和sa[2]的最长公共前缀是3,所以能够包括住前3个B串,所以能够所有累加起来;可是如果是小于等于的话,比如是1的话,那么2和3的值就须要又一次计算了,由于此时的最长公共前缀是1了,我们还须要一个num[]数组来记录此时大于等于栈顶的值的个数,由于这在之后如果有更小的时候。还须要把这些大于等于的再减掉。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
typedef long long ll;
using namespace std;
const int maxn = 200010;

int sa[maxn];
int t1[maxn], t2[maxn], c[maxn];
int rank[maxn], height[maxn];

void build_sa(int s[], int n, int m) {
    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] = 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 (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]] = y[sa[i-1]] == y[sa[i]] && y[sa[i-1]+j] == y[sa[i]+j] ?

 p-1 : p++;

        if (p >= n) break;
        m = p;
    }
}

void getHeight(int s[],int n) {
    int i, j, k = 0;
    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 (s[i+k] == s[j+k]) k++;
        height[rank[i]] = k;
    }
}

int r[maxn];
int st[maxn], num[maxn];
char str1[maxn], str2[maxn];
int k, len1, len2;

ll solve(int n, int k) {
    ll i, j, tp, ans = 0;
    ll tot, top;
    for (i = 1; i <= n; i++) {
        if (height[i] < k) tot = top = 0;
        else {
            tp = 0;
            if (sa[i-1] > len1) {
                tp = 1;
                tot += height[i] - k + 1;
            }

            while (top > 0 && st[top] >= height[i]) {
                tot -= num[top] * (st[top] - height[i]);
                tp += num[top];
                top--;
            }

            st[++top] = height[i];
            num[top] = tp;
            if (sa[i] < len1)
                ans += tot;
        }
    }

    for (i = 1; i <= n; i++) {
        if (height[i] < k) tot = top = 0;
        else {
            tp = 0;
            if (sa[i-1] < len1) {
                tp = 1;
                tot += height[i] - k + 1;
            }

            while (top > 0 && st[top] >= height[i]) {
                tot -= num[top] * (st[top] - height[i]);
                tp += num[top];
                top--;
            }

            st[++top] = height[i];
            num[top] = tp;
            if (sa[i] > len1)
                ans += tot;
        }
    }
    return ans;
}

int main() {
    int i, j;
    while (scanf("%d", &k) != EOF && k) {
        scanf("%s%s",str1,str2);
        for (i = 0; str1[i]; ++i)
            r[i] = str1[i];
        r[i] = ‘$‘,len1 = i,i++;
        for (j = 0; str2[j];j++)
            r[i+j] = str2[j];
        r[i+j] = 0;
        int len = i + j; 

        build_sa(r, len+1, 128);
        getHeight(r, len);

        printf("%lld\n", solve(len, k));
    }
    return 0;
}
时间: 2024-10-03 01:51:45

POJ - 3415 Common Substrings(后缀数组求长度不小于 k 的公共子串的个数+单调栈优化)的相关文章

POJ 3415 Common Substrings (求长度不小于k的公共子串的个数)

Common Substrings Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 10002   Accepted: 3302 Description A substring of a string T is defined as: T(i, k)=TiTi+1...Ti+k-1, 1≤i≤i+k-1≤|T|. Given two strings A, B and one integer K, we define S,

POJ 3415 Common Substrings(后缀数组求重复字串)

题目大意:给你两个字符串,让你求出来两个字符串之间的重复子串长度大于k的有多少个. 解题思路: 先说论文上给的解释:基本思路是计算A的所有后缀和B的所有后缀之间的最长公共前缀的长度,把最长公共前缀长度不小于k的部分全部加起来.先将两个字符串连起来,中间用一个没有出现过的字符隔开.按height值分组后,接下来的工作便是快速的统计每组中后缀之间的最长公共前缀之和.扫描一遍,每遇到一个B的后缀就统计与前面的A的后缀能产生多少个长度不小于k的公共子串,这里A的后缀需要用一个单调的栈来高效的维护.然后对

POJ 3415 Common Substrings(长度不小于k 的公共子串的个数--后缀数组+单调栈优化)

题意:给定两个字符串A 和B,求长度不小于k 的公共子串的个数(可以相同). 样例1: A="xx",B="xx",k=1,长度不小于k 的公共子串的个数是5. 样例2: A ="aababaa",B ="abaabaa",k=2,长度不小于k 的公共子串的个数是22. 思路: 如果i后缀与j后缀的LCP长度为L, 在L不小于K的情况下, 它对答案的贡献为L - K + 1. 于是我们可以将两个串连起来, 中间加个奇葩的分隔符

【POJ 3415】Common Substrings 长度不小于k的公共子串的个数

长度不小于k的公共子串的个数,论文里有题解,卡了一上午,因为sum没开long long!!! 没开long long毁一生again--- 以后应该早看POJ里的Discuss啊QAQ #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; const int N = 200003; int t1[N], t2[N], c[

POJ 3415 Common Substrings ——后缀数组

[题目分析] 判断有多少个长度不小于k的相同子串的数目. N^2显然是可以做到的. 其实可以维护一个关于height的单调栈,统计一下贡献,就可以了. 其实还是挺难写的OTZ. [代码] #include <cstdio> #include <cstring> #include <cmath> #include <cstdlib> #include <map> #include <set> #include <queue>

POJ 3415 Common Substrings 后缀数组+并查集

后缀数组,看到网上很多题解都是单调栈,这里提供一个不是单调栈的做法, 首先将两个串 连接起来求height   求完之后按height值从大往小合并.  height值代表的是  sa[i]和sa[i-1] 的公共前缀长度,那么每次合并就是合并  i和i-1 那么在合并小的时候公共前缀更大的肯定已经都合并在一起,那么就可以直接统计了. #include<iostream> #include<cstdio> #include<algorithm> #include<

POJ - 3415 Common Substrings 后缀数组+单调栈

一般遇到多串问题,就用不同的符号把他们接起来,当成一个串来处理. 如A串是"aaaba",B串是"abaa". 把height数组按照不小于K分组,假设K = 2.从前向后扫描,对于每一组中的每个B,考虑前面A对其的贡献. 可以用栈来维护A的值.当要入栈的height值大于栈顶的值,统计得到的子串的数目 (+= height[i]-K+1). 如果小于,总和减去多加的部分,pop到小于height[i]为止,再入栈算贡献.

POJ - 3261 Milk Patterns (后缀数组求可重叠的 k 次最长重复子串)

Description Farmer John has noticed that the quality of milk given by his cows varies from day to day. On further investigation, he discovered that although he can't predict the quality of milk from one day to the next, there are some regular pattern

poj 1743 二分答案+后缀数组 求不重叠的最长重复子串

题意:给出一串序列,求最长的theme长度 (theme:完全重叠的子序列,如1 2 3和1 2 3  or  子序列中每个元素对应的差相等,如1 2 3和7 8 9) 要是没有差相等这个条件那就好办多了,直接裸题. 一开始想了个2B方法,后来发现真心2B啊蛤蛤蛤 1 for i=1 to 88 do 2 { 3 for j=1 to length 4 { 5 r2[j]=r[j]+i; 6 if (r2[j]>88) r2[i]-=88; 7 } 8 把新序列r2连接到原序列r的后面 9 pr