第三周训练题解

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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值