您的位置:首页 > 其它

2013 多校第九场 hdu 4691 Front compression(暴力 + 剪枝 OR 后缀数组)

2013-08-20 20:28 411 查看
题目:http://acm.hdu.edu.cn/showproblem.php?pid=4691题目大意:输入一组字符串,对于输入的每一个字符串,输出一个数字加一个空格加一个字符串加一个回车,数字为它和上一个输入的字符串的公共前缀的长度,字符串为未匹配的字符串,空串的话长度也为1,最后输出两个数,分别是输入的所有的长度和和输出的长度和,空格和回车都算一个。思路:暴力枚举前缀,然后TLE,然后加了一个剪枝,就是:如果前一个字符串的起始位置和现在这个一样,那么就直接算出来。然后就这样神奇的 3000ms 飘过了。。 只能说测试数据里这样的数据太多了。。 = =这题的正解应该是后缀数组,今天花了一天去学了这个东西。 现在发现这题应该是后缀数组里很基础的了,前后两者的重叠部分的长度就是 min( lcp(pre_a,a), b - a ,pre_b - pre_a),当pre_a == a 是 lcp 里需要特判,然后 rank[ a ] > rank[ pre_a ] 交换一下,使 a 的rank 在前面,rmq 查询的区间应该是 rmq(rank[ a ] + 1,rank[ pre_a ]),注意,这里要+1,因为 height[ i ] 表示的是后缀 sa[i ] 和sa[ i - 1] 的公共前缀。暴力代码如下:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

typedef __int64 lld;

const int MAXN = 100011;

char str[MAXN];

int main()
{
while(~scanf("%s",str))
{
int n;
scanf("%d",&n);
int pre_a = -1 ,pre_b = -1;
int a,b;
lld ans1 = 0,ans2 = 0;
for(int i = 0;i<n;i++)
{
scanf("%d%d",&a,&b);
ans1 += (b-a+1);
if(a==pre_a)
{
if(b>pre_b)
{
ans2 += b - pre_b;
int len = pre_b - a;
if(len == 0 ) ans2 ++ ;
while(len)
{
ans2++;
len /= 10;
}
ans2 += 2;
}
else
{
int len = b - a;
if(len ==0) ans2 ++ ;
while(len)
{
ans2 ++ ;
len /= 10;
}
ans2 += 2;
}
}
else
{
int dis = 0;
for(int i = a,j = pre_a;i<b&&j<pre_b;i++,j++)
if(str[i]==str[j])
dis++;
else break;
int len = dis;
if(len == 0 ) ans2 ++ ;
while(len)
{
ans2++;
len /= 10;
}
ans2 += b - a - dis + 2;
}
pre_a = a;
pre_b = b;
}
printf("%I64d %I64d\n",ans1,ans2);
}
return 0;
}
后缀数组代码如下:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

typedef __int64 lld;

const int MAXN = 111111 ;

int t[MAXN],t2[MAXN],sa[MAXN],c[222];

char str[MAXN];

void build_sa(int n,int m)
{
int *x = t,*y = t2;
for(int i = 0;i<m;i++) c[i] = 0;
for(int i = 0;i<n;i++) c[x[i] = str[i]] ++;
for(int i = 1;i<m;i++) c[i] += c[i - 1];
for(int i = n - 1;i>=0;i--) sa[--c[x[i]]] = i;

for(int k = 1;k<n;k <<= 1)
{
int p = 0;

for(int i = n - k ;i<n;i++) y[p++] = i;
for(int i = 0;i<n;i++) if(sa[i] >= k) y[p++] = sa[i] - k;

for(int i = 0;i<m;i++) c[i] = 0;
for(int i = 0;i<n;i++) c[x[y[i]]] ++ ;
for(int i = 1;i<m;i++) c[i] += c[i - 1];
for(int i = n - 1;i>=0;i--) sa[--c[x[y[i]]]] = y[i];

swap(x,y);
x[sa[0]] = 0;
p = 1;
for(int i = 1;i<n;i++)
x[sa[i]] = y[sa[i]] == y[sa[i - 1]] && y[sa[i] + k] == y[sa[i - 1] + k] ? p - 1 : p++;
if(p>=n) break;
m = p;
}
}

int rank[MAXN],height[MAXN];

void get_height(int n)
{
for(int i = 1;i<=n;i++)
rank[sa[i]] = i;
int k = 0;
for(int i = 0;i<n;i++)
{
if(k) k--;
int j = sa[rank[i] - 1];
while(str[i + k] == str[j + k]) k ++;
height[rank[i]] = k;
}
// for(int i = 1;i<=n;i++)
//printf("i = %d,height[i] = %d,sa = %d\n",i,height[i],sa[i]);
}

int d[MAXN][111];

void init_rmq(int n)
{
for(int i = 1;i<=n;i++)
d[i][0] = height[i];
for(int j = 1;(1<<j) <= n;j++)
for(int i = 1;i + (1<<j) - 1 <=n;i++)
{
d[i][j] = min(d[i][j - 1] ,d[ i + (1<<(j - 1))][ j - 1 ]);
}
}

int rmq(int a,int b)
{
int k = 0;
while((1<<(k+1)) < (b - a + 1)) k++;
return min(d[a][k],d[b - (1<<k) + 1][k]);
}

int lcp(int a,int b,int n)
{
if(a == b) return n - a ;
a = rank[a],b = rank[b];
if(a>b) swap(a,b);
return rmq(a + 1 , b);
}

int main()
{
while(~scanf("%s",str))
{
int len = strlen(str);
build_sa(len+1,128);
get_height(len);
init_rmq(len);
int q;
scanf("%d",&q);
int a,b;
scanf("%d%d",&a,&b);
lld ans1 = b - a + 1;
lld ans2 = 1 + b - a + 2;
q--;
int pre_a = a,pre_b = b;
while(q--)
{
scanf("%d%d",&a,&b);
ans1 += b - a + 1;
//printf("lcp = %d\n",lcp(pre_a,a,len));
int tt = min(lcp(pre_a,a,len),min(b - a,pre_b - pre_a));
int tmp = tt;
if(tmp == 0) ans2 ++;
while(tmp)
{
ans2 ++;
tmp /= 10;
}
ans2 += b - a - tt + 2;
pre_a = a;
pre_b = b;
}
printf("%I64d %I64d\n",ans1,ans2);
}
return 0;
}

                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: