poj 3415 Common Substrings(后缀数组+单调栈+dp)

题目链接:点击打开链接

博主是看了后缀数组那篇论文后,然后做了这道题练手。

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

然后在网上搜了好几篇解题报告,都只提到了用单调栈来维护,而都没有讲清楚具体思路。感觉看得模模糊糊,很是不详细,花了不少时间在这个题目上,遂写下了这篇博文,希望大家能给大家带来帮助(本人语文水平不太好,轻喷)。

首先对于字符串的两个后缀ijlcp(最长公共子串),长度为rank[i]+1~rank[j]区间内height[]的最小值(不妨设rank[i])。按照height分组后,需要统计每一个新的后缀与前面的后缀带来的新的公共前缀,这里视为贡献度,这里如果是o(n^2)的复杂度的话,明显超时。所以想到统计新的后缀所带来的贡献的时候,能不能用前一个后缀所做的贡献来加快时间。我们用一个单调递增的栈来维护数据。

对于每一个新的后缀,如果heigh值大于栈顶元素:

例如:

(这里Sa[i]表示的字符串是什么不重要,重要的是动态规划思维过程~~~~)

Sa[1]:ab

Sa[2]:abc

Sa[3]:abcd

Sa[4]:abcde

若此时算到Sa[4]这个新的后缀和前面所有后缀(Sa[3],Sa[2],Sa[1])能带来多大贡献,首先对于(Sa[1]Sa[2])来说,因为height[4]>height[3](单调栈栈顶元素),根据lcp的计算方法,所以Sa[3]能和(Sa[1]Sa[2])产生的贡献值就等同于Sa[3]和(Sa[1]Sa[2])所产生的贡献值,Sa[4]能和Sa[3]产生的贡献值为(height[4]-k+1),所以Sa[4]能产生的新的贡献值=(Sa[3]产生的贡献值+height[4]-k+1)

如果height值大于栈顶元素值:

Sa[1]:a

Sa[2]:ab

Sa[3]:abc

Sa[4]:abcd

Sa[5]:abcde

Sa[6]:abcdef

Sa[7]:abc

Sa[8]:ab

若此时计算到Sa[7],height[7]<栈顶元素值height[6],若此时还按照上面的求解方法,明显多算了Sa[5],Sa[6]后面的后缀e,ef,所以这里要减去Stacksize[i]*(Stack[top]-height[i])这个多算的后缀,Stacksize数组的含义马上会说到,它是表示这一段Stack[i]这一段压缩了多少个数。因为height[7]<栈顶值,但我们要维护一个单调递增的栈,又因为lcp是这段区间的最小值,所以后面和它前面的lcp最大长度不会超过height[7],所以以后开始的后缀同这段区间的lcp不会超过height[7],Stacksize[top]表示严格递增的(height[i]<=3&&height[i]>Stack[top-1])区间含有几个数。后面按照这种规律依次递推就能出结果了~~~其实这就是一个统计上的Dp~~~~~

然后来说,这个题目就不难了,先用一个独特的字符连接两个字符串,求出后缀数组后分情况统计,先求出B的后缀有多少个和A的公共前缀,再统计A的后缀有多少个和B的公共前缀~~~~~~结果就出来了~~~~~~~

#include
#include
#include
using namespace std;
typedef long long llt;
const int maxn=200010;
int wa[maxn],wb[maxn],wsf[maxn],wv[maxn],sa[maxn];
int ranks[maxn],height[maxn],s[maxn];
char s1[maxn],s2[maxn],str[maxn];
int cmp(int *r,int a,int b,int k)
{
    return r[a]==r[b]&&r[a+k]==r[b+k];
}
void da(char *r,int n,int m)
{
    int i,j,p,*x=wa,*y=wb,*t;
    for(i=0;i=0;i--)  sa[--wsf[x[i]]]=i;
    p=1;
    j=1;
    for(;p=j)  y[p++]=sa[i]-j;
        for(i=0;i=0;i--)  sa[--wsf[wv[i]]]=y[i];
        t=x;
        x=y;
        y=t;
        x[sa[0]]=0;
        for(p=1,i=1;i0&&height[i]<=Stack[top-1])
                {
                    top--;
                    tot-=Stacksize[top]*(Stack[top]-height[i]);
                    cnt+=Stacksize[top];
                }
                Stack[top]=height[i];
                Stacksize[top++]=cnt;
                if(sa[i]>L1) sum+=tot;
            }
        }
        tot=top=0;
        for(int i=2; i<=n; i++)
        {
            if(height[i]L1) cnt++,tot+=height[i]-k+1;
                while(top>0&&height[i]<=Stack[top-1])
                {
                    top--;
                    tot-=Stacksize[top]*(Stack[top]-height[i]);
                    cnt+=Stacksize[top];
                }
                Stack[top]=height[i];
                Stacksize[top++]=cnt;
                if(sa[i]

你可能感兴趣的:(ACM/ICPC)