题目:http://acm.hdu.edu.cn/showproblem.php?pid=3374
题目大意:给你一个字符串,让你分别求出最小和最大表示法的起始位置,如果有多个,就求出最左边那个,然后分别求出它在同构串中的出现次数。
思路:最小最大表示法,两个一样,稍微变一下就好,然后找到位置后,最直接的就是直接做kmp,然后求出次数,由于扩大了两倍,最后那个长度为n的
字符串不算,所以kmp匹配的时候直接是 < len_t-1 就好。之后我去网上看了一下,发现很多是不做kmp的,直接用fail数组的性质,因为是同构串,看它的
最小循环节就好,其实重复次数两个是一样的,都等于最小循环节的出现次数(如果循环节存在的话,否则为1)。
题目大意:给你一个字符串,让你分别求出最小和最大表示法的起始位置,如果有多个,就求出最左边那个,然后分别求出它在同构串中的出现次数。
思路:最小最大表示法,两个一样,稍微变一下就好,然后找到位置后,最直接的就是直接做kmp,然后求出次数,由于扩大了两倍,最后那个长度为n的
字符串不算,所以kmp匹配的时候直接是 < len_t-1 就好。之后我去网上看了一下,发现很多是不做kmp的,直接用fail数组的性质,因为是同构串,看它的
最小循环节就好,其实重复次数两个是一样的,都等于最小循环节的出现次数(如果循环节存在的话,否则为1)。
直接kmp的代码如下:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN = 1000111;
char str[MAXN<<1],s[MAXN];
int fail[MAXN];
void get_fail(char *p)
{
fail[0] = -1;
int j = -1;
int len = strlen(p);
for(int i = 1;i < len;i++)
{
while(j >= 0 && p[i] != p[j+1]) j = fail[j];
if(p[i] == p[j+1]) j++;
fail[i] = j;
}
}
int kmp(char *t,char *p)
{
get_fail(p);
int len_t = strlen(t);
int len_p = strlen(p);
int j = -1;
int ans = 0;
for(int i = 0;i < len_t-1;i++)
{
while(j >= 0 && t[i] != p[j+1]) j = fail[j];
if(t[i] == p[j+1]) j++;
if(j == len_p-1)
{
ans++;
j = fail[j];
}
}
return ans;
}
int main()
{
while(~scanf("%s",str))
{
int n = strlen(str);
for(int i = 0;i < n;i++)
str[n+i] = str[i];
str[n+n] = '\0';
//puts(str);
int p1 = 0,p2 = 1,mat = 0;
while(p1 < n && p2 < n)
{
if(str[p1+mat] == str[p2+mat])
{
mat++;
if(mat == n)
break;
}
else
{
if(str[p1+mat] < str[p2+mat])
p2 += mat+1;
else p1 += mat+1;
if(p1 == p2) p2++;
mat = 0;
}
}
int ans1 = min(p1,p2);
for(int i = 0;i < n;i++)
s[i] = str[i+ans1];
s[n] = '\0';
int times1 = kmp(str,s);
p1 = 0,p2 = 1,mat = 0;
while(p1 < n && p2 < n)
{
if(str[p1+mat] == str[p2+mat])
{
mat++;
if(mat == n)
break;
}
else
{
if(str[p1+mat] < str[p2+mat])
p1 += mat+1;
else p2 += mat+1;
if(p1 == p2) p2++;
mat = 0;
}
}
int ans2 = min(p1,p2);
for(int i = 0;i < n;i++)
s[i] = str[i+ans2];
s[n] = '\0';
int times2 = kmp(str,s);
printf("%d %d %d %d\n",ans1+1,times1,ans2+1,times2);
}
return 0;
}
利用fail的代码如下:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN = 1000111;
char str[MAXN<<1],s[MAXN];
int fail[MAXN];
void get_fail(char *p)
{
fail[0] = -1;
int j = -1;
int len = strlen(p);
for(int i = 1;i < len;i++)
{
while(j >= 0 && p[i] != p[j+1]) j = fail[j];
if(p[i] == p[j+1]) j++;
fail[i] = j;
}
}
int main()
{
while(~scanf("%s",str))
{
get_fail(str);
int n = strlen(str);
for(int i = 0;i < n;i++)
str[n+i] = str[i];
str[n+n] = '\0';
//puts(str);
int p1 = 0,p2 = 1,mat = 0;
while(p1 < n && p2 < n)
{
if(str[p1+mat] == str[p2+mat])
{
mat++;
if(mat == n)
break;
}
else
{
if(str[p1+mat] < str[p2+mat])
p2 += mat+1;
else p1 += mat+1;
if(p1 == p2) p2++;
mat = 0;
}
}
int ans1 = min(p1,p2);
p1 = 0,p2 = 1,mat = 0;
while(p1 < n && p2 < n)
{
if(str[p1+mat] == str[p2+mat])
{
mat++;
if(mat == n)
break;
}
else
{
if(str[p1+mat] < str[p2+mat])
p1 += mat+1;
else p2 += mat+1;
if(p1 == p2) p2++;
mat = 0;
}
}
int ans2 = min(p1,p2);
int len = n-1-fail[n-1];
int times = 1;
if(n%len == 0)
times = n/len;
printf("%d %d %d %d\n",ans1+1,times,ans2+1,times);
}
return 0;
}