1. 程式人生 > >51nod 1732 婚姻介紹所 (字尾陣列、RMQ)

51nod 1732 婚姻介紹所 (字尾陣列、RMQ)

題目

這裡寫圖片描述

題解

學過後綴陣列的話,讀完題基本就可以直接寫了。(送分題?)

先求出字尾陣列和高度陣列,然後ST預處理lcp值的範圍最小值,最後O(1)查詢即可。

debug: 這個送分(命)題,我寫了兩個神奇的bug。 第一個是每次倍增排序後確定排名時cmp內應該比較的是sa[i-1]和sa[i].因為你要比較下第i名與第i-1名是否排名相同,肯定是比較第i名的字尾而不是i字尾。 第二個bug就更奇葩了,查詢區間最值時以2為底取對數,我寫成了log(L)???

AC程式碼

//#include <bits/stdc++.h>
#include <cstdio>
#include <algorithm> #include <cstring> #include <cmath> using namespace std; const int maxn=1e3+7; int k,sa[maxn],lcp[maxn],n,rk[maxn],tmp[maxn]; char s[maxn]; bool cmp(const int &i,const int &j) { if(rk[i]!=rk[j]) return rk[i]<rk[j]; int fi=i+k<n?rk[i+k]:-1
; int fj=j+k<n?rk[j+k]:-1; return fi<fj; } void get_sa() { for(int i=0;i<n;i++) { sa[i]=i; rk[i]=s[i]; } for(k=1;k<=n;k*=2) { sort(sa,sa+n,cmp); tmp[sa[0]]=0; for(int i=1;i<n;i++) tmp[sa[i]]=tmp[sa[i-1]]+(cmp(sa[i-1
],sa[i])?1:0); for(int i=0;i<n;i++) rk[i]=tmp[i]; } } void get_lcp() { for(int i=0;i<n;i++) rk[sa[i]]=i; int h=0; for(int i=0;i<n;i++) { if(rk[i]==n-1) { lcp[rk[i]]=0; h=0; continue; } int j=sa[rk[i]+1]; if(h) h--; for(;i+h<n && j+h<n;h++) if(s[i+h]!=s[j+h]) break; lcp[rk[i]]=h; } } int dp[maxn][20]; void rmq_init() { lcp[n-1]=0; memset(dp,0x3f,sizeof(dp)); for(int i=0;i<n;i++) dp[i][0]=lcp[i]; for(int j=1;(1<<j)<=n;j++) for(int i=0;i+(1<<j)<n;i++) dp[i][j]=min(dp[i][j-1],dp[i+(1<<(j-1))][j-1]); } int query(int x,int y) { int L=y-x+1; int k=(int)log2(L); return min(dp[x][k],dp[y-(1<<k)+1][k]); } int main() { int q; while(~scanf("%d",&n)) { scanf("%s",s); scanf("%d",&q); get_sa(); get_lcp(); rmq_init(); while(q--) { int x,y; scanf("%d%d",&x,&y); if(rk[x]>rk[y]) { int z=x; x=y; y=z; } if(x==y) { printf("%d\n",n-x); continue; } int ans=query(rk[x],rk[y]-1); printf("%d\n",ans); } } return 0; }