hdu 4691 后缀数组+RMQ+LCP

来源:互联网 发布:在淘宝上怎么做代销 编辑:程序博客网 时间:2024/04/27 20:59

题意:

输入文件:字符串+回车

输出文件:最长前缀数+空格+自己独有的串+回车


做法:

使用后缀数组求最长前缀(参考论文)。因为查询次数是大量的,所以在查询的过程中使用RMQ降低时间复杂度。


/*************************************************************************    > File Name: hdu4691.cpp    > Author: cy    > Mail: 1002@qq.com     > Created Time: 2014/10/8 19:08:17 ************************************************************************/#include<iostream>#include<cstring>#include <algorithm>#include<cstdlib>#include<vector>#include<cmath>#include<stdlib.h>#include<iomanip>#include<list>#include<deque>#include<map>#include <stdio.h>#include <queue>#define maxn 2000000+5#define inf 0x3f3f3f3f  #define INF 0x3FFFFFFFFFFFFFFFLL#define rep(i,n) for(i=0;i<n;i++) #define reP(i,n) for(i=1;i<=n;i++)#define ull unsigned long long #define ll long long#define cle(a) memset(a,0,sizeof(a))using namespace std;//后缀数组的数组们int sa[maxn], rank[maxn], height[maxn];int wa[maxn], wb[maxn], wv[maxn], wd[maxn];int val[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 n, int m)            //  倍增算法0(nlgn)。{    int i, j, p, *x = wa, *y = wb, *t;    for(i = 0; i < m; i ++) wd[i] = 0;    for(i = 0; i < n; i ++) wd[x[i]=r[i]] ++;    for(i = 1; i < m; i ++) wd[i] += wd[i-1];    for(i = n-1; i >= 0; i --) sa[-- wd[x[i]]] = i;    for(j = 1, p = 1; p < n; j *= 2, 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 ++) wd[i] = 0;        for(i = 0; i < n; i ++) wd[wv[i]] ++;        for(i = 1; i < m; i ++) wd[i] += wd[i-1];        for(i = n-1; i >= 0; i --) sa[-- wd[wv[i]]] = y[i];        for(t = x, x = y, y = t, 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 ++;        }    }}void calHeight(int *r, int n)            //  求height数组。{    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]; r[i+k] == r[j+k]; k ++);    }}char str[maxn];//数组的输入int A[maxn],B[maxn];//始末位置的输入int RMQ[maxn],num[maxn],dp[20][maxn];//RMQ相关数组void initRMQ(int n){num[0]=-1;int i;for(i=1;i<=n;i++){num[i]=((i&(i-1))==0)?num[i-1]+1:num[i-1];}for(i=1;i<=n;i++){dp[0][i]=i;//2的0次方等于本身}for(i=1;i<=num[n];i++){for(int j=1;j+(1<<i)-1<=n;j++){int a=dp[i-1][j];int b=dp[i-1][j+(1<<(i-1))];if(RMQ[a]<RMQ[b])dp[i][j]=a;else dp[i][j]=b;}}}int askRMQ(int a,int b)//返回下标{int t;if(a>b)swap(a,b);t=num[b-a+1];b-=(1<<t)-1;a=dp[t][a];b=dp[t][b];return RMQ[a]<RMQ[b]?a:b;}int lcp(int a,int b)//得到前缀{a=rank[a],b=rank[b];if(a>b)swap(a,b);return height[askRMQ(a+1,b)];//两个后缀的最长前缀}int getval(int n){if(n==0)return 1;int ans=0;while(n){ans++;n/=10;}return ans;}int main(){#ifndef ONLINE_JUDGE     freopen("in.txt","r",stdin);     //freopen("out.txt","w",stdout);#endif while(scanf("%s",&str)!=EOF) { int n=strlen(str); int i; //进行赋值 for(i=0;i<n;i++) { val[i]=str[i]-'a'+1; } val[n]=0; da(val,n+1,128); calHeight(val,n); for(i=1;i<=n;i++) { RMQ[i]=height[i];//赋值RMQ } initRMQ(n);//RMQ初始化 ll ans1=0,ans2=0; int k=0; scanf("%d",&k); for(i=1;i<=k;i++) { scanf("%d%d",&A[i],&B[i]); ans1+=(B[i]-A[i]+1);//空格和回车字符 if(i==1){ ans2=B[i]-A[i]+3; continue;//空格、回车、0的位数 } int temp; if(A[i]!=A[i-1])temp=lcp(A[i],A[i-1]);//比较开始位置 else temp=inf; temp=min(temp,min((B[i]-A[i]),(B[i-1]-A[i-1]))); ans2+=B[i]-A[i]-temp+2; ans2+=getval(temp);//加上位数 } printf("%I64d %I64d\n",ans1,ans2); }    return 0;}


0 0
原创粉丝点击