字尾排序學多了以後就只會字首排序了(輸出1-n的整數)
存個板子 倍增
\(code\ below:\)
#include #define ll long long
using namespace std;
const int maxn=1000000+10;
int n,m,sa[maxn],tax[maxn],rnk[maxn],tp[maxn],h[maxn];
char a[maxn];
void sa()
for(i=1;i<=n;i++) rnk[sa[i]]=i;
for(i=1;i<=n;i++)
}signed main()
題意:問本質不同的子串數量
其實就是減去相鄰字尾的 \(lcp\),即 \(height\) 陣列
\(code\ below:\)
#include using namespace std;
const int maxn=1000+10;
int n,m,sa[maxn],tax[maxn],rnk[maxn],tp[maxn],h[maxn],ans;
char a[maxn];
void sa()
for(int i=1;i<=n;i++) rnk[sa[i]]=i;
for(int i=1;i<=n;i++)
}int main()
return 0;
}
\(sp705\) 就是資料範圍開大一點,\(ans\) 開個 \(long\ long\)
題意:求最長重複 \(2\) 次不重疊子串
正解 \(o(n^2)\),但是我們可以用字尾陣列+二分優化到 \(o(n\log n)\)
處理出 \(height\) 陣列後二分長度,如果 \(height就重置,否則處理出連續一段區間的 \(minsa\) 和 \(maxsa\),若 \(maxsa-minsa>k\) 就說明有兩個子串
\(code\ below:\)
#include using namespace std;
const int maxn=50000+10;
int n,m,a[maxn],sa[maxn],tax[maxn],rnk[maxn],tp[maxn],h[maxn],ans;
void sa()
for(int i=1;i<=n;i++) rnk[sa[i]]=i;
for(int i=1;i<=n;i++)
}int check(int k)
} return 0;
}int main()
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i>1;
if(check(mid)) l=mid;
else r=mid-1;
} l++;
printf("%d\n",(l>=5)?l:0);
return 0;
}
題意:求重複次數最多的連續重疊子串
在 \(height\) 陣列上瞎搞
\(code\ below:\)
#include #include #include #include using namespace std;
const int maxn=100000+10;
int n,m,rmq[maxn][18],sa[maxn],tax[maxn],rnk[maxn],tp[maxn],h[maxn];
char a[maxn];
void sa()
for(int i=1;i<=n;i++) rnk[sa[i]]=i;
for(int i=1;i<=n;i++)
for(int i=1;i<=n;i++) rmq[i][0]=i;
for(int j=1;j<18;j++)
for(int i=1;i+(1return h[query(l+1,r)];
}int main()
sa();
int k,t,now=0,ans=0;
for(int i=1;i=0&&lcp(t,t+i)>=i-k%i) now++;
ans=max(ans,now);
}} printf("%d\n",ans+1);
} return 0;
}
題意:求 \(\frac-2\times \sum_
for(i=1;i<=n;i++) rnk[sa[i]]=i;
for(i=1;i<=n;i++)
}signed main()
top=0;
for(int i=n;i>=2;i--)
for(int i=2;i<=n;i++) ans-=2ll*l[i]*r[i]*h[i];
printf("%lld\n",ans);
return 0;
}題意:求每乙個 \(i\),字串 \(1-i\) 中本質不同串的個數
因為字符集很大,考慮用字尾陣列
我們們後面新增乙個字元換成刪除乙個字元,那麼就是在雙向鍊錶上刪除兩個 \(height\) 的 \(max\),保留兩個 \(height\) 的 \(min\),反著來一下
\(code\ below:\)
#include #define ll long long
using namespace std;
const int maxn=100000+10;
int n,m,a[maxn],mp[maxn],sa[maxn],tax[maxn],rnk[maxn],tp[maxn],h[maxn],pre[maxn],nxt[maxn];ll ans[maxn];
void sa()
for(i=1;i<=n;i++) rnk[sa[i]]=i;
for(i=1;i<=n;i++)
}int main()
sort(mp+1,mp+n+1);
int cnt=unique(mp+1,mp+n+1)-mp-1;
for(int i=1;i<=n;i++)
a[i]=lower_bound(mp+1,mp+cnt+1,a[i])-mp;
reverse(a+1,a+n+1);
sa();
for(int i=1;i<=n;i++) pre[i]=i-1,nxt[i]=i+1;
for(int i=1;i<=n;i++)
for(int i=n;i>=1;i--) ans[i]+=ans[i+1];
for(int i=n;i>=1;i--) printf("%lld\n",ans[i]);
return 0;
}
字尾陣列好題!
因為 \(r\) 相似是 \(r-1\) 相似但 \(r-1\) 相似不是 \(r\) 相似,我們考慮現將 \(height\) 從大到小排序,然後用並查集維護一下。因為 \(a_p\times a_q\) 取最大時 \(a_p\) 和 \(a_q\) 可能為負數,那麼我們記錄乙個最大值和最小值,每次相乘一下,更新答案。最終要求的方案數是 \(num\) 的字尾和,\(a_p\times a_q\) 最大時為 \(ans\) 的字尾最大值
\(code\ below:\)
#include #define int long long
#define ll long long
using namespace std;
const int maxn=600000+10;
int n,m,b[maxn],val[maxn],sa[maxn],tax[maxn],rnk[maxn],tp[maxn],h[maxn],fa[maxn],siz[maxn],max[maxn],min[maxn];ll num[maxn],ans[maxn];
char a[maxn];
void sa()
for(i=1;i<=n;i++) rnk[sa[i]]=i;
for(i=1;i<=n;i++)
}int find(int x)
void merge(int x,int y)
bool cmp(int x,int y)
signed main()
for(int i=0;i=0;i--) num[i]+=num[i+1],ans[i]=max(ans[i],ans[i+1]);
for(int i=0;ireturn 0;
}
字尾陣列學習筆記
要用好字尾陣列要先理解裡面幾個陣列的概念 sa i 表示字典序第i大的字尾下標 字典序排名依次是1 len stri ng ra nk i 表示下標為i的字尾字典序排名 he ight i 表示sa i 和sa i 1 最長公共字首的長度.乙個性質 lc p su ffix i suff ix j ...
字尾陣列 學習筆記
字尾陣列是處理字串的強有力的工具 在字串處理當中,字尾樹和字尾陣列都是非常有力的工具。其實字尾陣列是字尾樹的乙個非常精巧的替代品,它比字尾樹容易程式設計實現,能夠實現字尾樹的很多功能而時間複雜度也不太遜色,並且,它比字尾樹所占用的空間小很多。可以說,在資訊學競賽中字尾陣列比字尾樹要更為實用。我們定義...
字尾陣列 學習筆記
剛剛學完回文自動機 來學字尾陣列 一開始思路看得懂 但是 看不懂呀 一堆神仙 洛谷p3809 勿謂我,何強過者,炸哉!我們需要一種新的演算法 字尾陣列 首先,輸入字串 scanf s ch 1 n strlen ch 1 然後,按照題意 suffix sort ch for int i 1 i n ...