Crazy Search
POJ-1200
题目链接:http://poj.org/problem?id=1200
解题思路:用哈希来节省字符串比较的时间。
Description
Many people like to solve hard puzzles some of which may lead them to madness. One such puzzle could be finding a hidden prime number in a given text. Such number could be the number of different substrings of a given size that exist in the text. As you soon will discover, you really need the help of a computer and a good algorithm to solve such a puzzle.
Your task is to write a program that given the size, N, of the substring, the number of different characters that may occur in the text, NC, and the text itself, determines the number of different substrings of size N that appear in the text.
As an example, consider N=3, NC=4 and the text “daababac”. The different substrings of size 3 that can be found in this text are: “daa”; “aab”; “aba”; “bab”; “bac”. Therefore, the answer should be 5.
Input
The first line of input consists of two numbers, N and NC, separated by exactly one space. This is followed by the text where the search takes place. You may assume that the maximum number of substrings formed by the possible set of characters does not exceed 16 Millions.
Output
The program should output just an integer corresponding to the number of different substrings of size N found in the given text.
Sample Input
3 4
daababac
Sample Output
5
Hint
Huge input,scanf is recommended.
题解
#include<iostream>
#include<cstring>
#include<stdio.h>
#include<string.h>
using namespace std;
const int N = 16000003;
bool has[N];
int vis[500];
char str[N];
int main()
{
int n,nc,ans = 0,cnt = 0;
while(~scanf("%d%d%s",&n,&nc,str))
{
ans=0;
cnt=0;
int le=strlen(str);
// memset(has,0,sizeof(has));
// memset(vis,0,sizeof(vis));
for(int i=0;i<le;++i)
if(vis[str[i]]==0)
vis[str[i]]=cnt++;
for(int i=0;i<=le-n;++i)
{
int sum=0;
for(int j=0;j<n;++j)
sum=sum*cnt+vis[str[j+i]];
if(has[sum]==0)
{
has[sum]=1;
++ans;
}
}
printf("%d\n",ans);
}
return 0;
}
String Problem
HDU-3374
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3374
解题思路:本题通过求循环节来求最大最小字符串的个数
Problem Description
Give you a string with length N, you can generate N strings by left shifts. For example let consider the string “SKYLONG”, we can generate seven strings:
String Rank
SKYLONG 1
KYLONGS 2
YLONGSK 3
LONGSKY 4
ONGSKYL 5
NGSKYLO 6
GSKYLON 7
and lexicographically first of them is GSKYLON, lexicographically last is YLONGSK, both of them appear only once.
Your task is easy, calculate the lexicographically fisrt string’s Rank (if there are multiple answers, choose the smallest one), its times, lexicographically last string’s Rank (if there are multiple answers, choose the smallest one), and its times also.
Input
Each line contains one line the string S with length N (N <= 1000000) formed by lower case letters.
Output
Output four integers separated by one space, lexicographically fisrt string’s Rank (if there are multiple answers, choose the smallest one), the string’s times in the N generated strings, lexicographically last string’s Rank (if there are multiple answers, choose the smallest one), and its times also.
Sample Input
abcder aaaaaa ababab
Sample Output
1 1 6 1 1 6 1 6 1 3 2 3
Author
WhereIsHeroFrom
Source
HDOJ Monthly Contest – 2010.04.04
代码
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
#define N 1000017
int Next[N];
char str[N];
int k;
void getNext( char T[],int len)
{
int i = 0, j = -1;
Next[0] = -1;
while(i < len)
{
if(j == -1 || T[i] == T[j])
{
++i,++j;
Next[i] = j;
}
else
j = Next[j];
}
}
//最小表示法
int get_minstring(char *s)
{
int len = strlen(s);
int i = 0, j = 1, k = 0;
while(i<len && j<len && k<len)
{
int t=s[(i+k)%len]-s[(j+k)%len];
if(t==0)
++k;
else
{
if(t > 0)
i+=k+1;
else
j+=k+1;
if(i==j)
++j;
k=0;
}
}
return min(i,j);
}
//最大表示法
int get_maxstring(char *s)
{
int len = strlen(s);
int i = 0, j = 1, k = 0;
while(i<len && j<len && k<len)
{
int t=s[(i+k)%len]-s[(j+k)%len];
if(t==0)
++k;
else
{
if(t > 0)
j+=k+1;
else
i+=k+1;
if(i==j)
j++;
k=0;
}
}
return min(i,j);
}
int main()
{
while(scanf("%s",str)!=EOF)
{
int len = strlen(str);
getNext(str,len);
int tt = len - Next[len];
int num = 1;
if(len%tt == 0)
num = len/tt;
int posmin = get_minstring(str);
int posmax = get_maxstring(str);
printf("%d %d %d %d\n",posmin+1,num,posmax+1,num);
}
return 0;
}
Period
题目链接:POJ 1961 http://poj.org/problem?id=1961
解题思路:KMP的next[]可以求循环节
Description
For each prefix of a given string S with N characters (each character has an ASCII code between 97 and 126, inclusive), we want to know whether the prefix is a periodic string. That is, for each i (2 <= i <= N) we want to know the largest K > 1 (if there is one) such that the prefix of S with length i can be written as AK ,that is A concatenated K times, for some string A. Of course, we also want to know the period K.
Input
The input consists of several test cases. Each test case consists of two lines. The first one contains N (2 <= N <= 1 000 000) – the size of the string S.The second line contains the string S. The input file ends with a line, having the
number zero on it.
Output
For each test case, output “Test case #” and the consecutive test case number on a single line; then, for each prefix with length i that has a period K > 1, output the prefix size i and the period K separated by a single space; the prefix sizes must be in increasing order. Print a blank line after each test case.
Sample Input
3
aaa
12
aabaabaabaab
0
Sample Output
Test case #1
2 2
3 3
Test case #2
2 2
6 2
9 3
12 4
题解
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 1e6+10;
int next[N];
char b[N];
int n;
void getnext()
{
int i,j;
i=0;
j=-1;
next[0]=-1;
int len=strlen(b);
while(i<len)
{
if(j==-1||b[i]==b[j])
{
++i;
++j;
next[i]=j;
}
else
j=next[j];
}
}
int main()
{
int k=1;
while(scanf("%d",&n))
{
if(n==0)
break;
memset(next,0,sizeof(next));
memset(b,'0',sizeof(b));
scanf("%s",b);
getnext();
printf("Test case #%d\n",k++);
int i;
for(i=2;i<=n;i++)
if(next[i]!=0&&i%(i-next[i])==0)
printf("%d %d\n",i,i/(i-next[i]));
printf("\n");
}
return 0;
}
string matching
HDU - 6629 http://acm.hdu.edu.cn/showproblem.php?pid=6629
解题思路:EX-KMP
String matching is a common type of problem in computer science. One string matching problem is as following:
Given a string s[0…len−1], please calculate the length of the longest common prefix of s[i…len−1] and s[0…len−1] for each i>0.
I believe everyone can do it by brute force.
The pseudo code of the brute force approach is as the following:
We are wondering, for any given string, what is the number of compare operations invoked if we use the above algorithm. Please tell us the answer before we attempt to run this algorithm.
输入
The first line contains an integer T, denoting the number of test cases.
Each test case contains one string in a line consisting of printable ASCII characters except space.
- 1≤T≤30
- string length ≤106 for every string
输出
For each test, print an integer in one line indicating the number of compare operations invoked if we run the algorithm in the statement against the input string.
样例输入
复制样例数据
3
Happy_New_Year
ywwyww
zjczzzjczjczzzjc
样例输出
17
7
32
题解:
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll;
const int N=1e6+5;
char s[N];
int Next[N];
int extend[N];
void GetNext(char T[],int &m,int Next[])
{
int a=0,p=0;
Next[0]=m;
for(int i=1;i<m;++i)
if(i>=p || i+Next[i-a]>=p)
{
if(i>=p)
p=i;
while(p<m && T[p]==T[p-i])
++p;
Next[i]=p-i;
a=i;
}
else
Next[i]=Next[i-a];
}
void GetExtend(char S[],char T[],int extend[],int Next[])
{
int a=0,p=0;
int n=strlen(s);
int m=strlen(T);
GetNext(T,m,Next);
for(int i=0;i<n;++i)
if(i>=p||i+Next[i-a]>=p)
{
if(i>=p)
p=i;
while(p<n&&p-i<m&&S[p]==T[p-i])
++p;
extend[i]=p-i;
a=i;
}
else
extend[i]=Next[i-a];
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%s",s);
GetExtend(s+1,s,extend,Next);
int len=strlen(s)-1;
ll ans=0;
for(int i=0;i<len;++i)
if(i+extend[i]<len)
ans+=extend[i]+1;
else
ans+=extend[i];
printf("%lld\n",ans);
}
return 0;
}