poj 3415

来源:互联网 发布:安卓蓝牙调试助手 源码 编辑:程序博客网 时间:2024/05/21 23:27

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

       分析:首先,将两个串分别称为A串和B串,简单的做法是将B串放到A串后面,当然,中间要加一个用于分隔的字符,找一个比所有字母都大的就行了。接着求后缀数组,而容易知道,lcp(rank[i],rank[j])-k+1代表以i开头的A串后缀和以j开头B串后缀所能贡献的子串数量,这样,只要枚举i,j就能得到答案,但这样是O(n^2)的时间,显然是不能满足题意的。其实后缀数组的题目很多都是从height[]数组着手的,因此我们可以想到利用height[]数组呈波浪状而易于分组的特点,很容易将height[]数组分为若干组,使得每组内任意两个后缀的最长公共前缀都不小于k,由于每组都是独立的,我们只需要算出一组内满足题意的子串数量,再与其它组算出来的累加,结果就是答案,因此现在问题就是能否尽快的求解出每组的答案。

                 对某组,我们可以从前往后扫描,每扫到一个A串后缀,就与前面所有的B串后缀分别求最长公共前缀,并将结果累加,不过这样只求了这个A串后缀前面的结果,还有后面的呢?下意识的会想:再往后也扫过去不就行了。不过其实只要再对B串也执行一遍相同的操作就行了,效果一样的。但是这个过程也是o(n^2)的,要用height[]的特性和单调栈优化到o(n)。优化的过程感觉确实挺不好懂的,看了许久才明白...╮(╯▽╰)╭

                首先要明确,在height[]分组后,每一组内,i后缀和j后缀的最长公共前缀是height[i+1....j]之间的最小值,即lcp(i,j)=min( height[i+1...j] )。那么我们在某组内从前往后扫描时,当遇到一个A串后缀,只要能有一个数值total记录前面所有B串后缀能贡献的子串数量就好了。为了能实现total的记录,就需要用到一个单调栈了。栈内的每个元素需要存两个信息,一个是height值,一个是该height值所占权重。单调栈内元素按height值严格递增。具体处理过程是(以对A串操作为例),在分好的某组内进行遍历,每遇到一个height[i],若sa[i-1]在B串内,则让total加上height[i]-k+1,代表对答案的贡献值。接着看height[i]入栈是否会改变单调栈的单调性,若会,则对单调栈进行调整,这里要明白为什么要保持单调栈的单调性,因为这样保持了height值是递增的,于是当遇到一个A串后缀时,它与栈底元素对应的公共后缀是栈底的height值*权重,栈中其它元素也一样。这里的权重其实也表示该公共前缀一共有几个,因为如果height数组是1,2, 3,2...,当1,2,3入栈后,2会把2,3踢走,而此时2的权重就是3,表示前面曾经还有2,3,长度为2的公共前缀有3个。另外,3后面是2,因此2后面与3代表的后缀的公共前缀是2,故total需要减去遇到3时多加的那一部分。

    代码如下:

#include <cstdio>#include <stack>#include <set>#include <iostream>#include <string>#include <vector>#include <queue>#include <list>#include <functional>#include <cstring>#include <algorithm>#include <cctype>#include <string>#include <map>#include <iomanip>#include <cmath>#define LL long long#define ULL unsigned long long#define SZ(x) (int)x.size()#define MP(a, b) make_pair(a, b)#define MS(arr, num) memset(arr, num, sizeof(arr))#define PB push_back#define F first#define S second#define ROP freopen("input.txt", "r", stdin);#define MID(a, b) (a + ((b - a) >> 1))#define lson l,mid,rt<<1#define rson mid+1,r,rt<<1|1#define lrt rt << 1#define rrt rt << 1|1#define root 1,n,1#define BitCount(x) __builtin_popcount(x)#define BitCountll(x) __builtin_popcountll(x)#define LeftPos(x) 32 - __builtin_clz(x) - 1#define LeftPosll(x) 64 - __builtin_clzll(x) - 1const double PI = acos(-1.0);const int INF = 1e7;using namespace std;const double eps = 1e-5;const int MAXN = 300 + 10;const int MOD = 1000007;const double M=1e-8;const int N=200100;typedef pair<int, int> pii;typedef pair<int, string> pis;int k,sa[N],rank[N],height[N];int cnt[N],wa[N],wb[N],wv[N];bool cmp(int *y,int a,int b,int l){    return y[a]==y[b] && y[a+l]==y[b+l];}void da(char s[],int n,int m){    int i,j,p,*x=wa,*y=wb;    MS(cnt,0);    for (i=0;i<n;i++) cnt[x[i]=s[i]]++;    for (i=1;i<m;i++) cnt[i]+=cnt[i-1];    for (i=n-1;i>=0;i--) sa[--cnt[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]];        MS(cnt,0);        for (i=0;i<n;i++) cnt[wv[i]]++;        for (i=1;i<m;i++) cnt[i]+=cnt[i-1];        for (i=n-1;i>=0;i--) sa[--cnt[wv[i]]]=y[i];        swap(x,y);        for (i=1,p=1,x[sa[0]]=0;i<n;i++) {            x[sa[i]]=cmp(y,sa[i],sa[i-1],j)?p-1:p++;        }    }}void getHeight(char s[],int n){    int i,j,k=0;    for (i=1;i<=n;i++) rank[sa[i]]=i;    for (i=0;i<n;height[rank[i++]]=k)        for (k?k--:0,j=sa[rank[i]-1];s[i+k]==s[j+k];k++) ;}int st[N][2],len;void slove(char s[],int n){    int i,j;    LL sum=0,total=0,top=0;    for (i=2;i<n;i++) {           if (height[i]<k) total=top=0;  //分组        else {            int cnt=0;            if (sa[i-1]>len) {   // 若sa[i-]是B串                cnt=1;      // 代表权值                total+=height[i]-k+1;   //  total 记录出现过的B串的贡献值            }            while(top>0 && height[i]<=st[top-1][0]) {   // 保持单调性                top--;                total-=st[top][1]*(st[top][0]-height[i]);                cnt+=st[top][1];            }            st[top][0]=height[i];            st[top++][1]=cnt;            if (sa[i]<len) sum+=total;  // 若sa[i]是A串,加上之前所有B串的贡献值        }    }    total=top=0;  // 对B串执行相同的操作    for (i=2;i<n;i++) {        if (height[i]<k) total=top=0;        else {            int cnt=0;            if (sa[i-1]<len) {                cnt=1;                total+=height[i]-k+1;            }            while(top>0 && height[i]<=st[top-1][0]) {                top--;                total-=st[top][1]*(st[top][0]-height[i]);                cnt+=st[top][1];            }            st[top][0]=height[i];            st[top++][1]=cnt;            if (sa[i]>len) sum+=total;        }    }    printf("%lld\n",sum);}int main(){    int i,j,n;    while(~scanf("%d",&k),k)    {        char s[N];        scanf("%s",s);        len=strlen(s);        strcat(s,"|");        scanf("%s",s+len+1);        n=strlen(s);          // puts(s);        s[n]=0;        da(s,n+1,128);        getHeight(s,n);//        for (i=1;i<=n;i++) cout<<sa[i]<<" "; cout<<endl;//        for (i=0;i<n;i++) cout<<rank[i]<<" "; cout<<endl;//        for (i=1;i<=n;i++) cout<<height[i]<<" "; cout<<endl;        slove(s,n);    }}


0 0
原创粉丝点击