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