算法提高 着急的WYF
問題描述
WYF在戰網上的密碼非常複雜(含大小寫字母、數字以及特殊字符,如”!”,”@”,”{”等),但他很不巧地忘記了。現在他非常着急,都快飛起來了。他只記得他的密碼是某個字符串S的子串。現在問題來了,你要告訴他有多少種可能的密碼,以幫助他確定能在多少時間內完成枚舉並嘗試解密工作。
輸入格式
輸入僅包含一行,爲一個字符串S,不含空格。
輸出格式
輸出一個整數,表示可能的密碼數量。
樣例輸入
ToTal
樣例輸出
14
數據規模和約定
對於70%的數據,S的長度不超過1000;(暴力)
對於100%的數據,S的長度不超過15000。(Suffix Array)
—— 分割線 ——
分析:
題目已經給了提示,想要過所有的測試數據,必須要用到Suffix Array(後綴數組)。
由於之前我已經詳細地講解了關於後綴數組的相關知識,並介紹了其在求字符串子串個數上的應用,因此這裏我就不多說口水話了,直接上代碼(如果有沒看過那篇文章的同學,我強烈建議先看再做,這是鏈接【算法與數據結構】—— 後綴數組),本題的完整代碼如下:
#include<iostream>
using namespace std;
const int N=15010;
class SuffixArray{
private:
static const int MAX=N;
int wa[MAX],wb[MAX],wd[MAX],r[MAX]; //n表示字符串的長度
bool isSame(int *r,int a,int b,int len)
{ return r[a]==r[b] && r[a+len]==r[b+len]; }
void da(int n,int m)
{
int *x=wa,*y=wb,*t;
for(int i=0;i<m;i++) wd[i]=0;
for(int i=0;i<n;i++) wd[x[i]=r[i]]++;
for(int i=1;i<m;i++) wd[i]+=wd[i-1];
for(int i=n-1;i>=0;i--) sa[--wd[x[i]]]=i;
for(int j=1,p=1;p<n;j<<1,m=p){
//對第二關鍵字排序
p=0;
for(int i=n-j;i<n;i++) y[p++]=i;
for(int i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j;
//對第一關鍵字排序
for(int i=0;i<m;i++) wd[i]=0;
for(int i=0;i<n;i++) wd[x[i]]++;
for(int i=1;i<m;i++) wd[i]+=wd[i-1];
for(int i=n-1;i>=0;i--) sa[--wd[x[y[i]]]]=y[i];
//利用指針操作調換兩數組的內容
t=x,x=y,y=t;
//更新x數組
p=1,x[sa[0]]=0;
for(int i=1;i<n;i++) x[sa[i]]=isSame(y,sa[i-1],sa[i],j)?p-1:p++;
}
}
public:
int sa[MAX],rank[MAX],height[MAX],n;
void calSuffixArray(char *s) //計算後綴數組sa
{
n=0;
while(*s){
r[n++]=*s-'!'+1;
s++;
}
r[n]=0; //對於r數組而言,其還需要在最後加一個0方便處理
da(n+1,100);
}
void calRank() //計算名次數組rank
{ for(int i=1;i<=n;i++) rank[sa[i]]=i; }
void calHeight() //計算相鄰的兩個後綴的最長公共前綴長度數組height
{
int j,k=0;
calRank();
for(int i=0;i<n;i++){
if(k) k--;
int j=sa[rank[i]-1];
while(r[i+k]==r[j+k]) k++;
height[rank[i]]=k;
}
}
long long calSubstringNum(char *s) //對於某個字符串str,計算其不同子串的個數
{
calSuffixArray(s);
calHeight();
long long ans=0;
for(int i=1;i<=n;i++)
ans += n - sa[i] -height[i];
return ans;
}
};
int main()
{
char chs[N];
cin>>chs;
SuffixArray suffixArray;
cout<<suffixArray.calSubstringNum(chs)<<endl;
return 0;
}