题目大意:
给定一个整数
和一个表示消息的字符串
,找到至少出现
次的
的最长子字符串。
如果存在多个解决方案,则优选最右边出现的子串(即样例
)。
由多组数据,当
时输入结束。其中
、
。
如果没有解决方案,则输出
;否则,输出两个整数,用空格分隔,第一个整数表示出现至少
次的子串的最大长度; 第二个整数给出了这种子串最可能的起始位置(同时要注意**题目中字串从
开始**)。
------------------------------------------------
分析:
啊,本蒟蒻不会后缀数组咋办啊……可不要忘了我们还有
+二分这种好东西!!
既然我们使得至少出现
次的子串的长度尽量大,所以我们可以二分出一个
。若对其
结果为
,则用
记录。
关于
函数,我们可以每次找长度为
的字串,即
。用
存储每个子串出现的次数,若某一个子串出现数
,则返回
;否则返回
。
找到
后,再计算一下它出现的位置即可。(方法与
差不多)
------------------------------------------------
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; }