HDU 5558 后缀数组+二分

时间:2023-02-22 10:54:51

题意有一些绕,但其实就是对于不断变化的i,求以j(0=j<i)使得suffix[j]与suffix[i]的最长公共前缀最长,如果有多个j,则取最小的j。

可以在rank数组中二分,在1-rank[i-1]中二分最接近i的j使得sa[j]小于i,通俗地说就是rank比的rank[i]小,并且位于i之前的后缀。因为这个是左边最接近rank[i]的,所以与suffix[i]的最长公共前缀一定是满足最大的。接下来再根据得到的LCP值,二分一个最小的j。同理,再从rank[i+1]到rank[n]中作类似二分。两者结合得到当前的K和T。

其实这道题中间过程也可以不用二分,直接向左右暴力扫。速度会快很多。

 #include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <string.h>
#include <stdio.h>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <cmath>
#include <ctime>
#include <cassert>
#include <sstream>
using namespace std; const int N=1e5+; int MIN(int a,int b) {
return a<b?a:b;
}
int MAX(int a,int b) {
return a>b?a:b;
} int val[N];
struct RMQ {
int dp[N][];
int (*cmp) (int,int);
void setMin() {
cmp=MIN;
}
void setMax() {
cmp=MAX;
}
void init(int n,int *val) {
for (int i=; i<=n; i++)
dp[i][]=val[i];
for (int j=; (<<j)<=n; j++) {
int k=<<(j-);
for (int i=; i+k<=n; i++)
dp[i][j]=cmp(dp[i][j-],dp[i+k][j-]);
}
}
int query(int a,int b) {
if (a>b) swap(a,b);
int dis=b-a+;
int k=log((double)dis)/log(2.0);
return cmp(dp[a][k],dp[b-(<<k)+][k]);
}
} rmq; char s[N];
struct SuffixArray {
int wa[N], wb[N], cnt[N], wv[N];
int rk[N], height[N];
int sa[N];
bool cmp(int r[], int a, int b, int l) {
return r[a] == r[b] && r[a+l] == r[b+l];
}
void calcSA(char r[], int n, int m) {
int i, j, p, *x = wa, *y = wb;
for (i = ; i < m; ++i) cnt[i] = ;
for (i = ; i < n; ++i) cnt[x[i]=r[i]]++;
for (i = ; i < m; ++i) cnt[i] += cnt[i-];
for (i = n-; i >= ; --i) sa[--cnt[x[i]]] = i;
for (j = , p = ; p < n; j *= , m = p) {
for (p = , i = n - j; i < n; ++i) y[p++] = i;
for (i = ; i < n; ++i) if (sa[i] >= j) y[p++] = sa[i] - j;
for (i = ; i < n; ++i) wv[i] = x[y[i]];
for (i = ; i < m; ++i) cnt[i] = ;
for (i = ; i < n; ++i) cnt[wv[i]]++;
for (i = ; i < m; ++i) cnt[i] += cnt[i-];
for (i = n-; i >= ; --i) sa[--cnt[wv[i]]] = y[i];
for (swap(x, y), p = , x[sa[]] = , i = ; i < n; ++i)
x[sa[i]] = cmp(y, sa[i-], sa[i], j) ? p- : p++;
}
}
void calcHeight(char r[], int n) {
int i, j, k = ;
for (i = ; i <= n; ++i) rk[sa[i]] = i;
for (i = ; i < n; height[rk[i++]] = k)
for (k?k--:, j = sa[rk[i]-]; r[i+k] == r[j+k]; k++);
}
int lcp(int a,int b,int len) {
if (a==b) return len-a;
int ra=rk[a],rb=rk[b];
if (ra>rb) swap(ra,rb);
return queryST(ra+,rb);
}
int st[N][];
int preLog2[N];
void initST(int n) {
for (int i=; i<=n; i++)
st[i][]=height[i];
for (int j=; (<<j)<=n; j++) {
int k=<<(j-);
for (int i=; i+k<=n; i++)
st[i][j]=min(st[i][j-],st[i+k][j-]);
}
preLog2[]=;
for(int i=;i<=n;i++){
preLog2[i]=preLog2[i-]+((i&(i-))==);
}
}
int queryST(int a,int b) {
if (a>b) swap(a,b);
int dis=b-a+;
int k=preLog2[dis];
return min(st[a][k],st[b-(<<k)+][k]);
}
void solve(int n) {
calcSA(s,n+,);
calcHeight(s,n);
initST(n);
rmq.setMin();
rmq.init(n,sa);
printf("-1 %d\n",s[]);
int i=;
while (i<n) {
int l=,r=rk[i]-;
int k=-,t=s[i];
int minIndex=-;
while (l<=r) {
int mid=(l+r)>>;
int minSA=rmq.query(mid,rk[i]-);
if (minSA<i)
l=mid+,minIndex=minSA;
else
r=mid-;
}
if (minIndex!=-) {
int u=lcp(minIndex,i,n);
if (u) {
k=u;
int l=,r=rk[i]-,ret=-;
while (l<=r) {
int mid=(l+r)>>;
int curLCP=lcp(sa[mid],i,n);
if (curLCP>=u)
r=mid-,ret=mid;
else l=mid+;
}
t=rmq.query(ret,rk[i]-);
}
}
l=rk[i]+;
r=n;
minIndex=-;
while (l<=r) {
int mid=(l+r)>>;
int minSA=rmq.query(rk[i]+,mid);
if (minSA<i)
r=mid-,minIndex=minSA;
else
l=mid+;
}
if (minIndex!=-) {
int u=lcp(minIndex,i,n);
if (u&&u>=k) {
if (u==k)
t=min(t,minIndex);
else
t=minIndex;
k=u;
int l=rk[i]+,r=n,ret=-;
while (l<=r) {
int mid=(l+r)>>;
int curLCP=lcp(sa[mid],i,n);
if (curLCP>=u)
l=mid+,ret=mid;
else r=mid-;
}
t=min(t,rmq.query(rk[i]+,ret));
}
}
if (k==-) {
printf("-1 %d\n",s[i]);
i++;
} else {
printf("%d %d\n",k,t);
i+=k;
}
}
}
} suf; int main () {
int T;
scanf("%d",&T);
while (T--) {
scanf("%s",s);
int n=strlen(s);
static int cas=;
printf("Case #%d:\n",cas++);
suf.solve(n);
}
return ;
}