题目链接
题意:
给了一个字符串,Si是由下标i到字符串末尾加上0到i-1组成的字符串,只有当字符串Si和Sj是相同的才算是在一组,问字符串可以分成多少组并按字典序最小的输出每组中的i下标。
思路:
看到这题想到了哈希,然后哈希改来改去改到95%死活不过。还有9分钟的时候队友想出来了正解,可惜来不及敲了QAQ||
这题最后是如果字符串是有循环节,并且完全由循环节构成的才能进行分组。
如果没有完全由循环节构成的话,就像这样的图,将字符串复制一份到后面,会发现中间是断开的连不上。
所以知道了这点就好做了。用KMP来算循环节的大小就好了。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=1e6+5;
char t[maxn];
int ne[maxn];
void makenext(const char p[],int ne[],int len)
{
ne[0]=0;
for(int i=1,k=0;i<len;i++)
{
while(k>0&&p[i]!=p[k]) k=ne[k-1];
if(p[i]==p[k]) k++;
ne[i]=k;
}
}
int main()
{
int len;
scanf("%s",t);
len=strlen(t);
makenext(t,ne,len);
int c=len-ne[len-1];//循环节
if(len%c==0&&ne[len-1]!=0)
{
printf("%d\n",c);
int r=len/c;//每组的个数
for(int i=0;i<c;i++)
{
int pos=i;
printf("%d ",r);
for(int j=1;j<=r;j++)
{
if(j==r)
printf("%d\n",pos);
else
printf("%d ",pos);
pos+=c;
}
}
}
else
{
printf("%d\n",len);
for(int i=1;i<=len;i++)
{
printf("1 ");
printf("%d\n",i-1);
}
}
return 0;
}