日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

题解 UVA10298 【Power Strings】

發布時間:2025/3/21 19 豆豆
生活随笔 收集整理的這篇文章主要介紹了 题解 UVA10298 【Power Strings】 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

此題我寫的是后綴數組SA解法,如果不會后綴數組的可以跳過本篇blog了。

參考文獻:羅穗騫 2009集訓隊后綴數組論文

前記

最近學后綴數組,肝了不少題,也分出了后綴數組的幾個題型,看這題沒有后綴數組的解法,于是我決定來水一波。

注:思想正確,代碼不一定正確。

分析題意

給定一個字符串 L,已知這個字符串是由某個字符串 S 重復 R 次而得到的,
求 R 的最大值。

其實就是求字符串L的連續重復子串。連續重復子串就是后綴數組的一個題型。

算法分析

1、我們需要最大的R,就說明我們需要連續重復子串的子串長度最小,那我們就可以首先枚舉子串的長度k。

2、如何判斷枚舉出的子串是否符合題意,為L的連續重復子串。相信學過后綴數組的大家一定知道如何求最長公共前綴,我在這里簡要提一下。

最長公共前綴

定義&&性質

一、

Suf(i)為字符串S的從i位開始的后綴。

例:S="LAHAKIOI"

Suf(0)="LAHAKIOI".Suf(4)="AKIOI".(字符串從0開始)

二、

height[i]為Suf(SA[i])和Suf(SA[i-1])的最長公共前綴

三、

h[i]=height[rank[i]],也就是 Suf(i)和在它前一名的后綴的最長公共前
綴。

h數組有以下性質:
h[i]≥h[i-1]-1

具體證明這里不給出,如果有需要可以看我blog里的后綴數組全講。

四、

Suf(j)和Suf(k)的最長公共前綴為(height[rank[j]+1],height[rank[j]+2],height[rank[j]+3],……,height[rank[k]])的最小值。

具體證明依然不給出,有需要可以看我的blog。


正所謂我們使用后綴數組求出了SA數組和Rank數組,我們想要求最長公共前綴,就要高效求出height數組。

求height數組,我們可以根據h數組的性質(三),對于每一個后綴和他上一個的字符串,我們不需要從頭開始尋找他們的最長公共前綴,這樣時間復雜度高達O(n^2),我們可以直接從h[i-1]-1處開始判斷,因為根據h數組的性質,這兩個字符串的前h[i-1]-1位一定是相同的。

這樣時間復雜度降到O(n).

這里可能大家看的不是很懂,不過代碼比較簡單,比較容易懂:

void get_height(){ //求height數組int k=0;for(int i=0;i<n;i++){if(k)k--; //h數組可以直接用一個計數器代替int j=sa[rank[i]-1];if(rank[i]-1==-1)continue; //因為我的排名從0開始,所以排名是0時要特判while(s[i+k]==s[j+k])k++; //從k處找最長公共前綴height[rank[i]]=k; //記錄height數組} }

那我們求出了最長公共前綴就可以判斷長度為k的子串是否滿足為連續最長子串。

判斷長度為k的子串是否滿足為連續最長子串

1、首先,我們要判斷字符串長度n是否能整除k,如果不能就顯然不行。

2、再看Suf(0)到Suf(k)的最長公共前綴是否為n-k。前面根據性質四,我們可以知道Suf(0)到Suf(k)的最長公共前綴,但每一次的時間都為O(n),整體下來時間復雜度十分高。

但我們可以看到Suf(0)是固定的,所以我們可以預處理出只需求出 height 數組中的每一個數到height[rank[0]]之間的最小值記為ans數組即可。

全部代碼:

#include<string> #include<stdio.h> #include<string.h> #include<iostream> #define maxn 300001 using namespace std; char s[maxn]; int n,sa[maxn],rank[maxn],newRK[maxn],key2[maxn],sum[maxn],height[maxn],k; int level; void get_sum(int m){for(int i=0;i<=m;i++) sum[i]=0;for(int i=0;i<n;i++) sum[rank[i]]++;for(int i=0;i<m;i++) sum[i]+=sum[i-1]; } bool cmp(int x,int y,int L){if(rank[x]!=rank[y])return false;if((x+L>=n&&y+L<n)||(x+L<n&&y+L>=n))return false;if(x+L>=n&& y+L>=n) return true;return rank[x+L] == rank[y+L]; } void get_height(){ //求height數組int k=0;for(int i=0;i<n;i++){if(k)k--; //h數組可以直接用一個計數器代替int j=sa[rank[i]-1];if(rank[i]-1==-1)continue; //因為我的排名從0開始,所以排名是0時要特判while(s[i+k]==s[j+k])k++; //從k處找最長公共前綴height[rank[i]]=k; //記錄height數組} } void Suffix_Sort(){ //SA板子for(int i=0;i<n;i++) rank[i]=s[i];get_sum(356);for(int i=n-1;i>=0;i--)sa[--sum[rank[i]]]=i;int w=1,m=max(n,356);while(w<n){int p=0;for(int i=n-w;i<n;i++)key2[p++]=i; //第二關鍵字越界排前for(int i=0;i<n;i++)if(sa[i]>=w)key2[p++]=sa[i]-w;//如果當前長度有第一關鍵字就記錄//以上按第二關鍵字排序get_sum(m);for(int i=n-1;i>=0;i--){int j=key2[i];sa[--sum[rank[j]]]=j;}//以上按第一關鍵字排序,直接覆蓋之前的sa數組,不需要再開一個key1newRK[sa[0]]=0;level=1;for(int i=1;i<n;i++){if(cmp(sa[i-1],sa[i],w))newRK[sa[i]]=level-1;elsenewRK[sa[i]]=level++;}for(int i=0;i<n;i++)rank[i]=newRK[i];//以上計算長度2*w的rank數組if (level==n)break;w<<=1;} } int main(){while(1){int ans[maxn];memset(sa,0,sizeof(sa));memset(height,0,sizeof(height));memset(ans,0,sizeof(ans));memset(rank,0,sizeof(rank));scanf("%s",s);n=strlen(s);if(s[0]=='.')break;Suffix_Sort();get_height();//下面求ans數組int a=rank[0],minx=10000000;for(int i=a;i>=1;i--){minx=min(minx,height[i]);ans[sa[i-1]]=minx;}minx=10000000;for(int i=a+1;i<=n;i++){minx=min(minx,height[i]);ans[sa[i]]=minx;}for(int i=1;i<=n;i++){if(n%i==0){if(ans[i]==n-i){printf("%d\n",n/i);break;}}}} }

謝謝觀賞

轉載于:https://www.cnblogs.com/hyfhaha/p/10678022.html

總結

以上是生活随笔為你收集整理的题解 UVA10298 【Power Strings】的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。