当前位置: 代码迷 >> 综合 >> 【解题报告】SPOJ - DISUBSTR Distinct Substrings 后缀数组
  详细解决方案

【解题报告】SPOJ - DISUBSTR Distinct Substrings 后缀数组

热度:91   发布时间:2024-01-29 14:14:18.0

求字符串不同子串的个数:

感觉可以字符串hash,复杂度是O(n^2) 应该还是可以过的,但是用后缀数组更好

后缀数组解法:考虑排名为i的后缀,其对子串的贡献为n-sa[i]+1,但是我们会发现这里面有和之前重复的,所以要减去这些重复的,而height[i]就是sa[i]和sa[i-1]之间重复的个数。所以最后的答案就是对(n-sa[i]+1-height[i])求和

AC代码:

#include <cstdio>
#include <cstring>
#include <algorithm> 
using namespace std;
typedef unsigned long long ULL;
typedef long long LL;
const ULL base=2333;
const int maxn=2e4+10;
int hash1[maxn];
int bin1[maxn];
int n,k;
ULL get_s(int left,int right)
{return hash1[right]-hash1[left-1]*bin1[right-left+1];
}
bool check(int len)
{ULL rec[maxn];memset(rec,0,sizeof(rec)); for(int i=1;i+len-1<=n;i++){rec[i]=get_s(i,i+len-1);}sort(rec+1,rec+n-len+1);int ans=1;for(int i=2;i<=n-len+1;i++){if(rec[i]==rec[i-1]) ++ans;else ans=1;if(ans>=k) return true;}return false;
}
void solve()
{int left=1,right=n;int ans=0;while(left<=right){int mid=(left+right)>>1;if(check(mid)) left=mid+1,ans=mid;else right=mid-1;}printf("%d\n",ans);
}
int main()
{while(scanf("%d%d",&n,&k)!=EOF){ULL S=0;bin1[0]=1;for(int i=1;i<=n;i++){int num;scanf("%d",&num);S=S*base+num;hash1[i]=S;bin1[i]=bin1[i-1]*base;}solve();}
}
  相关解决方案