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); }}
- poj 3415
- POJ 3415
- poj 3415
- poj 3415 , poj 3581,BZOJ 3538
- poj 3415 Common Substrings
- poj 3415 Common Substrings
- poj 3415 Common Substrings
- POJ 3415 Common Substrings
- POJ-3415-Common Substrings
- POJ 3415 && HDU 4416
- POJ 3415 Common Substrings
- POJ 3415 Common Substrings
- poj 3415 Common Substrings
- POJ 3415 Common Substrings
- POJ 3415Common Substrings
- poj 3415 Common Substrings
- POJ 3415 Common Substrings
- POJ
- 《剑指Offer》面试题:合并两个排序的链表
- quick-cocos2d-x数据存储之GameState
- 8583报文解析
- 联考1day1总结
- [C++11/14] 自动类型推导——auto
- poj 3415
- 纯CSS气泡框实现方法探究
- 和为s的连续正数序列
- centos安装jdk环境yum
- 部分手机RadioButton作为Tab键不居中问题
- quick-cocos2d-x基于源码加密打包功能的更新
- JAVA 二叉树遍历
- 数据结构 - 图
- 翻转单词顺序