题解 SP10079
题目大意:
给定一个整数 和一个表示消息的字符串 ,找到至少出现 次的 的最长子字符串。
如果存在多个解决方案,则优选最右边出现的子串(即样例 )。
由多组数据,当 时输入结束。其中 、 。
如果没有解决方案,则输出 ;否则,输出两个整数,用空格分隔,第一个整数表示出现至少 次的子串的最大长度; 第二个整数给出了这种子串最可能的起始位置(同时要注意**题目中字串从 开始**)。
------------------------------------------------
分析:
啊,本蒟蒻不会后缀数组咋办啊……可不要忘了我们还有 +二分这种好东西!!
既然我们使得至少出现 次的子串的长度尽量大,所以我们可以二分出一个 。若对其 结果为 ,则用 记录。
关于 函数,我们可以每次找长度为 的字串,即 。用 存储每个子串出现的次数,若某一个子串出现数 ,则返回 ;否则返回 。
找到 后,再计算一下它出现的位置即可。(方法与 差不多)
------------------------------------------------
Code(高清***)
#include<cstdio> #include<cstring> #include<map> using namespace std; #define rg register int #define I inline int #define V inline void #define ll long long #define db double #define B inline bool #define F1(i,a,b) for(rg i=a;i<=b;++i) #define ed putchar('\n') #define bl putchar(' ') const int N=40005; const int mod=13331; template<typename TP>V read(TP &x) { TP f=1;x=0;register char c=getchar(); for(;c<'0'||c>'9';c=getchar()) if(c=='-') f=-1; for(;c>='0'&&c<='9';c=getchar()) x=(x<<3)+(x<<1)+(c^'0'); x*=f; } template<typename TP>V print(TP x) { if(x<0) x=-x,putchar('-'); if(x>9) print(x/10); putchar(x%10+'0'); } int t,len,ans; char s[N]; unsigned ll p[N],H[N]; template<typename TP>B check(TP x) { map<int,int>m; for(rg i=1;i+x-1<=len;++i) { rg h=H[i+x-1]-H[i-1]*p[x];++m[h]; if(m[h]>=t) return true; } return false; } template<typename TP>I calc(TP x) { map<int,int>m; rg pos; for(rg i=1;i+x-1<=len;++i) { rg h=H[i+x-1]-H[i-1]*p[x];++m[h]; if(m[h]>=t) pos=i; } return pos-1;//题目中从0开始 } int main() { while(~scanf("%d",&t)&&t) { scanf("%s",s+1); p[0]=1,H[0]=ans=0,len=strlen(s+1); F1(i,1,len) { p[i]=p[i-1]*mod; H[i]=H[i-1]*mod+(s[i]-'a'+1); } rg l=1,r=len; while(l<=r) { rg mid=(l+r)>>1; if(check(mid)) l=mid+1,ans=mid; else r=mid-1; } if(!ans) puts("none"); else print(ans),bl,print(calc(ans)),ed; } return 0; }