POJ 2758 后缀数组
2008-08-04 19:15
459 查看
思路:
1.如果没有操作I,也就是insert操作,则很容易想到用后缀数组处理,直接计算lcp(i,j)即可,要注意的是用rmq计算lcp[i][j]的时候,如果i==j,要做特殊处理!
2.题目有个很容易误解的地方,就是Q操作的时候是对原始下标,而I操作的p是插入后的下标
3.计算Q(i,j)的时候,首先计算lcp(i,j),
1)如果lcp(i,j)大于离i,j最近的插入字符到i,j的距len, 则定位到i+len, j+len, 比较i+len,j+len处的插入字符,从而问题转换为lcp(i',j'),重复即可。
2)如果lcp(i,j)小于等于len,则答案即为lcp(i,j);
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
#define Min(a,b) (a)<(b)?(a):(b)
const int N = 51000;
const int M = 260;
int n, m;
char s
;
struct
{
char c;
int p;
}si[M];
int cnt
, mem[4]
, *rank, *nrank, *sa, *nsa, lcp[17]
;
// lcp[i][j]: longest commen prefix ( suffix(sa[k+1]), suffix(sa[k]) ) j <= k < j+2^i
void radix_sort()
{
int i, j, k;
rank = mem[0];
nrank = mem[1];
sa = mem[2];
nsa = mem[3];
for(i = 0; i < n; i++) cnt[s[i]]++;
for(i = 1; i < M; i++) cnt[i] += cnt[i-1];
for(i = n-1; i >= 0; i--) sa[--cnt[s[i]]] = i;
for(rank[0]=0, i=1; i < n; i++)
{
rank[sa[i]] = rank[sa[i-1]];
if(s[sa[i]]!=s[sa[i-1]]) rank[sa[i]]++;
}
for(k = 1; k<n && rank[sa[n-1]] < n-1; k*=2)
{
for(i = 0; i < n; i++) cnt[rank[sa[i]]] = i+1;
for(i = n-1; i >= 0; i--) if(sa[i]-k>=0)
nsa[--cnt[rank[sa[i]-k]]] = sa[i]-k;
// max(sa[i]-k)=n-k-1 , therefore i = n-k;
for(i = n-k; i < n; i++)
nsa[--cnt[rank[i]]] = i;
for(nrank[nsa[0]], i=1; i < n; i++)
{
nrank[nsa[i]] = nrank[nsa[i-1]];
if(rank[nsa[i]] != rank[nsa[i-1]]
|| rank[nsa[i]+k] != rank[nsa[i-1]+k])
nrank[nsa[i]]++;
}
swap(rank, nrank);
swap(sa, nsa);
}
}
void print()
{
for(int i=0;i<n;i++) printf("%d %s/n", sa[i], s + sa[i]);puts("");
puts(s);for(int i=0;i<n;i++) printf("%d ", rank[i]);puts("");
}
void get_lcp_rmq()
{
int i, j, k;
for(i=0,k=0; i<n; i++)
{
if(rank[i]==n-1) lcp[0][rank[i]]=k=0;
else
{
if(k>0)k--;
j = sa[rank[i]+1];
for(;s[i+k]==s[j+k];k++) ;
lcp[0][rank[i]]=k;
}
}
for(i=0,k=1; k<n; k*=2, i++)
{
for(j = 0; j+k < n; j++)
lcp[i+1][j] = Min(lcp[i][j], lcp[i][j+k]);
}
}
int rmq(int a, int b)
{
int i, j, k;
a = rank[a];
b = rank[b];
if(a>b) swap(a,b);
int t=b-a;
for(i=0,k=1; 2*k<t; i++,k*=2) ;
return Min(lcp[i][a], lcp[i][b-k]); // not b-k+1
// lcp[0][i]: LCP(i,i+1)
}
/*
aaaaaaaa
I a 2
I b 3
Q 1 3
*/
int query(int a, int b)
{
int i,j,k;
int mi, ret=0;
int x, y, t;
char c1, c2;
for(x = 0; si[x].p <= a; x++);
for(y = 0; si[y].p <= b; y++);
if(a==b) return n-a+m-x-2;
while(1)
{
k = rmq(a, b);
i = si[x].p-a;
j = si[y].p-b;
t = Min(k, Min(i,j));
ret+=t; a+=t; b+=t;
if(t==i || t==j)
{
while(si[x].p==a&&si[y].p==b)
{
if(si[x].c==si[y].c) x++,y++,ret++;
else return ret;
}
while(si[x].p==a)
{
if(si[x].c==s[b]) x++,b++,ret++;
else return ret;
}
while(si[y].p==b)
{
if(si[y].c==s[a]) y++,a++,ret++;
else return ret;
}
}
else return ret;
}
return ret;
}
void insert(char ch, int pos)
{
int i;
for(i = 0; i < m; i++)
{
if(si[i].p>=pos)break;
else pos--;
}
if(pos >= n) pos = n-1;
int j=i;
for(i = m++; i > j; i--) si[i] = si[i-1];
si[j].c = ch;
si[j].p = pos;
}
int main()
{
int i, j, k;
char cmd[10];
scanf("%s", s);
n = strlen(s);
s[n++]=0;
radix_sort();
get_lcp_rmq();
si[0].c = 0;
si[0].p = N;
m = 1;
scanf("%d", &k);
while(k--)
{
scanf("%s", cmd);
if(cmd[0]=='Q')
{
scanf("%d%d", &i, &j);
printf("%d/n", query(i-1,j-1));
}
else
{
scanf("%s%d", cmd, &i);
insert(cmd[0], i-1);
}
}
return 0;
}
1.如果没有操作I,也就是insert操作,则很容易想到用后缀数组处理,直接计算lcp(i,j)即可,要注意的是用rmq计算lcp[i][j]的时候,如果i==j,要做特殊处理!
2.题目有个很容易误解的地方,就是Q操作的时候是对原始下标,而I操作的p是插入后的下标
3.计算Q(i,j)的时候,首先计算lcp(i,j),
1)如果lcp(i,j)大于离i,j最近的插入字符到i,j的距len, 则定位到i+len, j+len, 比较i+len,j+len处的插入字符,从而问题转换为lcp(i',j'),重复即可。
2)如果lcp(i,j)小于等于len,则答案即为lcp(i,j);
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
#define Min(a,b) (a)<(b)?(a):(b)
const int N = 51000;
const int M = 260;
int n, m;
char s
;
struct
{
char c;
int p;
}si[M];
int cnt
, mem[4]
, *rank, *nrank, *sa, *nsa, lcp[17]
;
// lcp[i][j]: longest commen prefix ( suffix(sa[k+1]), suffix(sa[k]) ) j <= k < j+2^i
void radix_sort()
{
int i, j, k;
rank = mem[0];
nrank = mem[1];
sa = mem[2];
nsa = mem[3];
for(i = 0; i < n; i++) cnt[s[i]]++;
for(i = 1; i < M; i++) cnt[i] += cnt[i-1];
for(i = n-1; i >= 0; i--) sa[--cnt[s[i]]] = i;
for(rank[0]=0, i=1; i < n; i++)
{
rank[sa[i]] = rank[sa[i-1]];
if(s[sa[i]]!=s[sa[i-1]]) rank[sa[i]]++;
}
for(k = 1; k<n && rank[sa[n-1]] < n-1; k*=2)
{
for(i = 0; i < n; i++) cnt[rank[sa[i]]] = i+1;
for(i = n-1; i >= 0; i--) if(sa[i]-k>=0)
nsa[--cnt[rank[sa[i]-k]]] = sa[i]-k;
// max(sa[i]-k)=n-k-1 , therefore i = n-k;
for(i = n-k; i < n; i++)
nsa[--cnt[rank[i]]] = i;
for(nrank[nsa[0]], i=1; i < n; i++)
{
nrank[nsa[i]] = nrank[nsa[i-1]];
if(rank[nsa[i]] != rank[nsa[i-1]]
|| rank[nsa[i]+k] != rank[nsa[i-1]+k])
nrank[nsa[i]]++;
}
swap(rank, nrank);
swap(sa, nsa);
}
}
void print()
{
for(int i=0;i<n;i++) printf("%d %s/n", sa[i], s + sa[i]);puts("");
puts(s);for(int i=0;i<n;i++) printf("%d ", rank[i]);puts("");
}
void get_lcp_rmq()
{
int i, j, k;
for(i=0,k=0; i<n; i++)
{
if(rank[i]==n-1) lcp[0][rank[i]]=k=0;
else
{
if(k>0)k--;
j = sa[rank[i]+1];
for(;s[i+k]==s[j+k];k++) ;
lcp[0][rank[i]]=k;
}
}
for(i=0,k=1; k<n; k*=2, i++)
{
for(j = 0; j+k < n; j++)
lcp[i+1][j] = Min(lcp[i][j], lcp[i][j+k]);
}
}
int rmq(int a, int b)
{
int i, j, k;
a = rank[a];
b = rank[b];
if(a>b) swap(a,b);
int t=b-a;
for(i=0,k=1; 2*k<t; i++,k*=2) ;
return Min(lcp[i][a], lcp[i][b-k]); // not b-k+1
// lcp[0][i]: LCP(i,i+1)
}
/*
aaaaaaaa
I a 2
I b 3
Q 1 3
*/
int query(int a, int b)
{
int i,j,k;
int mi, ret=0;
int x, y, t;
char c1, c2;
for(x = 0; si[x].p <= a; x++);
for(y = 0; si[y].p <= b; y++);
if(a==b) return n-a+m-x-2;
while(1)
{
k = rmq(a, b);
i = si[x].p-a;
j = si[y].p-b;
t = Min(k, Min(i,j));
ret+=t; a+=t; b+=t;
if(t==i || t==j)
{
while(si[x].p==a&&si[y].p==b)
{
if(si[x].c==si[y].c) x++,y++,ret++;
else return ret;
}
while(si[x].p==a)
{
if(si[x].c==s[b]) x++,b++,ret++;
else return ret;
}
while(si[y].p==b)
{
if(si[y].c==s[a]) y++,a++,ret++;
else return ret;
}
}
else return ret;
}
return ret;
}
void insert(char ch, int pos)
{
int i;
for(i = 0; i < m; i++)
{
if(si[i].p>=pos)break;
else pos--;
}
if(pos >= n) pos = n-1;
int j=i;
for(i = m++; i > j; i--) si[i] = si[i-1];
si[j].c = ch;
si[j].p = pos;
}
int main()
{
int i, j, k;
char cmd[10];
scanf("%s", s);
n = strlen(s);
s[n++]=0;
radix_sort();
get_lcp_rmq();
si[0].c = 0;
si[0].p = N;
m = 1;
scanf("%d", &k);
while(k--)
{
scanf("%s", cmd);
if(cmd[0]=='Q')
{
scanf("%d%d", &i, &j);
printf("%d/n", query(i-1,j-1));
}
else
{
scanf("%s%d", cmd, &i);
insert(cmd[0], i-1);
}
}
return 0;
}
相关文章推荐
- poj 2758 后缀数组
- poj 3693 后缀数组 重复次数最多的连续重复子串
- poj 3415 后缀数组 两个字符串中长度不小于 k 的公共子串的个数
- poj 1743 Musical Theme 后缀数组
- POJ 1743 Musical Theme 后缀数组 楼教主男人八题之一
- poj 2406(Power Strings)后缀数组
- POJ 1743 Musical Theme(后缀数组[不可重叠最长重复子串])
- POJ 3415 Common Substrings 后缀数组 + 单调栈维护
- Poj 2774 Long Long Message|后缀数组
- [贪心+后缀数组] poj 3623 Best Cow Line, Gold
- poj 3693 后缀数组 重复次数最多的连续重复子串
- POJ 1226 Substrings 后缀数组
- POJ - 3261 Milk Patterns 后缀数组 二分
- poj 2774 Long Long Message 后缀数组模板
- POJ 3693 Maximum repetition substring(后缀数组求最长重复子串)
- poj1745(后缀数组+二分)
- Poj 3294 Life Forms (后缀数组 + 二分 + Hash)
- POJ 2774 Long Long Message 后缀数组
- POJ - 3693 Maximum repetition substring(后缀数组求重复次数最多的连续重复子串)
- poj 1743 后缀数组