现在的位置: 首页 > 综合 > 正文

【 题集 】 【kuangbin带你飞】专题十六 KMP & 扩展KMP & Manacher

2019年02月19日 ⁄ 综合 ⁄ 共 13498字 ⁄ 字号 评论关闭

    这次的题目多的有点变态啊!

   
链接

    我才做了8个模版体- -#,这几天慢慢补上去、

    所谓的next数组: next[n] 表示从字符串前n个字符的前缀后缀的共有长度。

A - Number Sequence

Description

Given two sequences of numbers : a[1], a[2], ...... , a[N], and b[1], b[2], ...... , b[M] (1 <= M <= 10000, 1 <= N <= 1000000). Your task is to find a number K which make a[K] = b[1], a[K + 1] = b[2], ...... , a[K + M - 1] = b[M].
If there are more than one K exist, output the smallest one. 
 

Input

The first line of input is a number T which indicate the number of cases. Each case contains three lines. The first line is two numbers N and M (1 <= M <= 10000, 1 <= N <= 1000000). The second line contains N integers which indicate
a[1], a[2], ...... , a[N]. The third line contains M integers which indicate b[1], b[2], ...... , b[M]. All integers are in the range of [-1000000, 1000000]. 
 

Output

For each test case, you should output one line which only contain K described above. If no such K exists, output -1 instead. 
 

Sample Input

2 13 5 1 2 1 2 3 1 2 3 1 3 2 1 2 1 2 3 1 3 13 5 1 2 1 2 3 1 2 3 1 3 2 1 2 1 2 3 2 1
 

Sample Output

6 -1

    返回匹配到的第一个字母的位置。

    

#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <string.h>
#include <math.h>
#include <queue>
#include <vector>
#include <map>
using namespace std;


#define N 1000010
#define M 10010

int next[N];
int S[N],p[M];  // S是主串,p是模式串
int a, b;

void get_next()
{
	int i = 0,j = -1;
	next[0] = -1;
	int len = b;

	while(i < len)
	{
		if(j == -1 || p[i] == p[j])
		{
			i ++;
			j ++;
	        next[i] = j;
		}
		else
		   j = next[j];
	}
}

int KMP()
{
	get_next();
	int i = 0, j = 0;
	int len = a;
	int m = b;
	while(i < len && j < m)
	{
	    if(j == -1 || S[i] == p[j])
	    {
		i++;
		j++;
	    }
	    else
		j = next[j];
            if(j == m)
            {
                return i - m + 1; //若匹配到,直接返回可以匹配的字符的位置。
            }
	}
	return -1;  // 若不匹配,返回 -1。
}

int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        while(n --)
        {
            scanf("%d%d",&a,&b);
            for(int i = 0; i < a; i ++)
            {
                scanf("%d",&S[i]);
            }
            for(int i = 0; i < b; i ++)
            {
                scanf("%d",&p[i]);
            }

            printf("%d\n",KMP());
        }
    }
	return 0;
}

B - Oulipo

Description

The French author Georges Perec (1936�1982) once wrote a book, La disparition, without the letter 'e'. He was a member of the Oulipo group. A quote from the book: 

Tout avait Pair normal, mais tout s’affirmait faux. Tout avait Fair normal, d’abord, puis surgissait l’inhumain, l’affolant. Il aurait voulu savoir où s’articulait l’association qui l’unissait au roman : stir son tapis, assaillant à tout instant son imagination,
l’intuition d’un tabou, la vision d’un mal obscur, d’un quoi vacant, d’un non-dit : la vision, l’avision d’un oubli commandant tout, où s’abolissait la raison : tout avait l’air normal mais… 

Perec would probably have scored high (or rather, low) in the following contest. People are asked to write a perhaps even meaningful text on some subject with as few occurrences of a given “word” as possible. Our task is to provide the jury with a program that
counts these occurrences, in order to obtain a ranking of the competitors. These competitors often write very long texts with nonsense meaning; a sequence of 500,000 consecutive 'T's is not unusual. And they never use spaces. 

So we want to quickly find out how often a word, i.e., a given string, occurs in a text. More formally: given the alphabet {'A', 'B', 'C', …, 'Z'} and two finite strings over that alphabet, a word W and a text T, count the number of occurrences of W in T. All
the consecutive characters of W must exactly match consecutive characters of T. Occurrences may overlap. 

 

Input

The first line of the input file contains a single number: the number of test cases to follow. Each test case has the following format: 

One line with the word W, a string over {'A', 'B', 'C', …, 'Z'}, with 1 ≤ |W| ≤ 10,000 (here |W| denotes the length of the string W). 
One line with the text T, a string over {'A', 'B', 'C', …, 'Z'}, with |W| ≤ |T| ≤ 1,000,000. 

 

Output

For every test case in the input file, the output should contain a single number, on a single line: the number of occurrences of the word W in the text T. 

 

Sample Input

3 BAPC BAPC AZA AZAZAZA VERDI AVERDXIVYERDIAN
 

Sample Output

1 3 0

    

    计算模式串在主串中出现过几次,代码中用count 计数,当匹配时,自加一,也就是输出的答案。

#include<stdio.h>
#include<string.h>

int next[1000010];
char S[1000010],p[10010];

void get_next()
{
	int i=0,j=-1;
	next[0]=-1;
	int len=strlen(p);
	while(i<len)
	{
		if(j==-1 || p[i]==p[j])
		{
		    i++;
		    j++;
	            next[i]=j;
		}
		else
		   j=next[j];
	}
}

int KMP()
{
	get_next();
	int i=0,j=0,count=0;
	int len=strlen(S);
	int m=strlen(p);
	while(i<len && j<m)
	{
	    if(j==-1 || S[i]==p[j])
	    {
	        i++;
		j++;
	    }
	    else
		j=next[j];
            if(j==m)   
            {
        	count++;
        	j=next[j];
            }
	}
	return count;
}

int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        while(n --)
        {
            scanf("%s%s",p,S);
            printf("%d\n",KMP());
        }
    }
	return 0;
}

C - 剪花布条

Description

一块花布条,里面有些图案,另有一块直接可用的小饰条,里面也有一些图案。对于给定的花布条和小饰条,计算一下能从花布条中尽可能剪出几块小饰条来呢? 
 

Input

输入中含有一些数据,分别是成对出现的花布条和小饰条,其布条都是用可见ASCII字符表示的,可见的ASCII字符有多少个,布条的花纹也有多少种花样。花纹条和小饰条不会超过1000个字符长。如果遇见#字符,则不再进行工作。 
 

Output

输出能从花纹布中剪出的最多小饰条个数,如果一块都没有,那就老老实实输出0,每个结果之间应换行。 
 

Sample Input

abcde a3 aaaaaa aa #
 

Sample Output

0 3
 

    这题和B类似,但有不同的地方,匹配完需要重置。

#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <string.h>
#include <math.h>
#include <queue>
#include <vector>
#include <map>
using namespace std;

#define N 1010

int next[N];
char S[N],p[N]; // S是主串,p是模式串

void get_next()
{
	int i = 0,j = -1;
	next[0] = -1;
	int len = strlen(p);

	while(i < len)
	{
		if(j == -1 || p[i] == p[j])
		{
			i ++;
			j ++;
	        next[i] = j;
		}
		else
		   j = next[j];
	}
}

int KMP()
{
	get_next();
	int i = 0, j = 0, count = 0;
	int len = strlen(S);
	int m = strlen(p);

	while(i < len && j < m)
	{
		if(j == -1 || S[i] == p[j])
		{
			i++;
			j++;
		}
		else
		   j = next[j];
            if(j == m)
            {
        	count ++;
        	j = 0;  //重置的地方
            }
	}
	return count;
}

int main()
{
    int n;
    while(~scanf("%s",S))
    {
        if(S[0] == '#')
            break;
        scanf("%s",p);
        printf("%d\n",KMP());
    }
	return 0;
}

D - Cyclic Nacklace

Description

CC always becomes very depressed at the end of this month, he has checked his credit card yesterday, without any surprise, there are only 99.9 yuan left. he is too distressed and thinking about how to tide over the last days. Being
inspired by the entrepreneurial spirit of "HDU CakeMan", he wants to sell some little things to make money. Of course, this is not an easy task. 

As Christmas is around the corner, Boys are busy in choosing christmas presents to send to their girlfriends. It is believed that chain bracelet is a good choice. However, Things are not always so simple, as is known to everyone, girl's fond of the colorful
decoration to make bracelet appears vivid and lively, meanwhile they want to display their mature side as college students. after CC understands the girls demands, he intends to sell the chain bracelet called CharmBracelet. The CharmBracelet is made up with
colorful pearls to show girls' lively, and the most important thing is that it must be connected by a cyclic chain which means the color of pearls are cyclic connected from the left to right. And the cyclic count must be more than one. If you connect the leftmost
pearl and the rightmost pearl of such chain, you can make a CharmBracelet. Just like the pictrue below, this CharmBracelet's cycle is 9 and its cyclic count is 2: 



Now CC has brought in some ordinary bracelet chains, he wants to buy minimum number of pearls to make CharmBracelets so that he can save more money. but when remaking the bracelet, he can only add color pearls to the left end and right end of the chain, that
is to say, adding to the middle is forbidden. 
CC is satisfied with his ideas and ask you for help.
 

Input

The first line of the input is a single integer T ( 0 < T <= 100 ) which means the number of test cases. 
Each test case contains only one line describe the original ordinary chain to be remade. Each character in the string stands for one pearl and there are 26 kinds of pearls being described by 'a' ~'z' characters. The length of the string Len: ( 3 <= Len <= 100000
).
 

Output

For each case, you are required to output the minimum count of pearls added to make a CharmBracelet.
 

Sample Input

3 aaa abca abcde
 

Sample Output

0 2 5

    

    求出最小环,然后再看需要加几个字符,使得题目中给出的串是循环的。

    容易得出最小环 = len - next[len]。

    

#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <string.h>
#include <math.h>
#include <queue>
#include <vector>
#include <map>
using namespace std;

#define N  100010

int next[N];
char S[N],p[N]; // S是主串,p是模式串

void get_next()
{
	int i = 0,j = -1;
	next[0] = -1;
	int len = strlen(p);

	while(i < len)
	{
		if(j == -1 || p[i] == p[j])
		{
			i ++;
			j ++;
	        next[i] = j;
		}
		else
		   j = next[j];
	}
}

int KMP()
{
	get_next();
	int i = 0, j = 0, count = 0;
	int len = strlen(S);
	int m = strlen(p);

	while(i < len && j < m)
	{
		if(j == -1 || S[i] == p[j])
		{
			i++;
			j++;
		}
		else
		   j = next[j];
            if(j == m)
            {
        	count ++;
        	j = 0;
            }
	}
	return count;
}

int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        while(n --)
        {
            scanf("%s",p);
            int len = strlen(p);
            get_next();

            int whi = len - next[len];

            //printf("%d\n",whi);
            if(whi == len)   // 这里易出错,容易忽略,和下面的判断条件混起来
                printf("%d\n",len);
            else if(len % whi == 0)
                printf("0\n");
            else
                printf("%d\n",whi - len%whi);
        }

    }
	return 0;
}

F - The Minimum Length

Description

There is a string A. The length of A is less than 1,000,000. I rewrite it again and again. Then I got a new string: AAAAAA...... Now I cut it from two different position and get a new string B. Then, give you the string B, can you tell me the length of the
shortest possible string A. For example, A="abcdefg". I got abcd efgabcdefgabcdefgabcdefg.... Then I cut the red part: efgabcdefgabcde as string B. From B, you should find out the shortest A.

Input

Multiply Test Cases. For each line there is a string B which contains only lowercase and uppercase charactors. The length of B is no more than 1,000,000.

Output

For each line, output an integer, as described above.

Sample Input

bcabcab
efgabcdefgabcde

Sample Output

3
7

  

    D 会做,这题也就一定会做了,比D 还少了一步

    

#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <string.h>
#include <math.h>
#include <queue>
#include <vector>
#include <map>
using namespace std;

#define N  1000010

int next[N];
char S[N],p[N]; // S是主串,p是模式串

void get_next()
{
	int i = 0,j = -1;
	next[0] = -1;
	int len = strlen(p);

	while(i < len)
	{
		if(j == -1 || p[i] == p[j])
		{
			i ++;
			j ++;
	        next[i] = j;
		}
		else
		   j = next[j];
	}
}

int KMP()
{
	get_next();
	int i = 0, j = 0, count = 0;
	int len = strlen(S);
	int m = strlen(p);

	while(i < len && j < m)
	{
		if(j == -1 || S[i] == p[j])
		{
			i++;
			j++;
		}
		else
		   j = next[j];
            if(j == m)
            {
        	count ++;
        	j = 0;
            }
	}
	return count;
}

int main()
{
    int n;
    while(~scanf("%s",p))
    {
        int len = strlen(p);
        get_next();

        int whi = len - next[len];

        printf("%d\n",whi);
    }
	return 0;
}

H - Seek the Name, Seek the Fame

Description

The little cat is so famous, that many couples tramp over hill and dale to Byteland, and asked the little cat to give names to their newly-born babies. They seek the name, and at the same time seek the fame. In order to escape from such boring job, the innovative
little cat works out an easy but fantastic algorithm: 

Step1. Connect the father's name and the mother's name, to a new string S. 
Step2. Find a proper prefix-suffix string of S (which is not only the prefix, but also the suffix of S). 

Example: Father='ala', Mother='la', we have S = 'ala'+'la' = 'alala'. Potential prefix-suffix strings of S are {'a', 'ala', 'alala'}. Given the string S, could you help the little cat to write a program to calculate the length of possible prefix-suffix strings
of S? (He might thank you by giving your baby a name:) 

Input

The input contains a number of test cases. Each test case occupies a single line that contains the string S described above. 

Restrictions: Only lowercase letters may appear in the input. 1 <= Length of S <= 400000. 

Output

For each test case, output a single line with integer numbers in increasing order, denoting the possible length of the new baby's name.

Sample Input

ababcababababcabab
aaaaa

Sample Output

2 4 9 18
1 2 3 4 5

    本题其实就是考察对next数组的理解吧。我用的是ans数组来存答案。

    

#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <string.h>
#include <math.h>
#include <queue>
#include <vector>
#include <map>
using namespace std;

#define N  400010

int next[N];
int ans[N];
char S[N],p[N]; // S是主串,p是模式串

void get_next()
{
	int i = 0,j = -1;
	next[0] = -1;
	int len = strlen(p);

	while(i < len)
	{
		if(j == -1 || p[i] == p[j])
		{
			i ++;
			j ++;
	        next[i] = j;
		}
		else
		   j = next[j];
	}
}

int KMP()
{
	get_next();
	int i = 0, j = 0, count = 0;
	int len = strlen(S);
	int m = strlen(p);

	while(i < len && j < m)
	{
		if(j == -1 || S[i] == p[j])
		{
			i++;
			j++;
		}
		else
		   j = next[j];
            if(j == m)
            {
        	count ++;
        	j = 0;
            }
	}
	return count;
}

int main()
{
    int n;
    while(~scanf("%s",p))
    {
        int len = strlen(p);
        int tmp_1 = len;
        get_next();
        int cnt = 0;
        while(next[len])
        {
            len = next[len];
            ans[cnt ++] = len;
        }
        if(ans[cnt - 1] != 0)
        printf("%d ",ans[cnt - 1]);
        for(int i = cnt - 2 ; i >= 0; i --)
        {
            printf("%d ",ans[i]);
        }
        printf("%d \n", tmp_1);  // 一开始这里PE了一发,最后这样输出还是第一次见
    }
	return 0;
}

J - Simpsons’ Hidden Talents

Description

Homer: Marge, I just figured out a way to discover some of the talents we weren’t aware we had. 
Marge: Yeah, what is it? 
Homer: Take me for example. I want to find out if I have a talent in politics, OK? 
Marge: OK. 
Homer: So I take some politician’s name, say Clinton, and try to find the length of the longest prefix 
in Clinton’s name that is a suffix in my name. That’s how close I am to being a politician like Clinton 
Marge: Why on earth choose the longest prefix that is a suffix??? 
Homer: Well, our talents are deeply hidden within ourselves, Marge. 
Marge: So how close are you? 
Homer: 0! 
Marge: I’m not surprised. 
Homer: But you know, you must have some real math talent hidden deep in you. 
Marge: How come? 
Homer: Riemann and Marjorie gives 3!!! 
Marge: Who the heck is Riemann? 
Homer: Never mind. 
Write a program that, when given strings s1 and s2, finds the longest prefix of s1 that is a suffix of s2.
 

Input

Input consists of two lines. The first line contains s1 and the second line contains s2. You may assume all letters are in lowercase.
 

Output

Output consists of a single line that contains the longest string that is a prefix of s1 and a suffix of s2, followed by the length of that prefix. If the longest such string is the empty string, then the output should be 0. 
The lengths of s1 and s2 will be at most 50000.
 

Sample Input

clinton homer riemann marjorie
 

Sample Output

0 rie 3

    给出两个字符串,求第一个字符串的前缀和第二个 字符串的后缀的最大可能相同的长度,并且输出相同部分

    如果把两个字符串连接起来,就直接变成next 数组的用法了,连接的时候,调用strcat函数,需要注意的是,当连接起来之后,next[len] 有可能大于两个字符串的长度,所以你要继续求next。

#include<cstdio>
#include<algorithm>
#include<string.h>
using namespace std;

#define MAX 50010

char str1[MAX << 1];
char str2[MAX];
int next[MAX << 1];

void get_next(char *p)
{
	int i = 0,j = -1;
	next[0] = -1;
	int len = strlen(p);
	while(i < len)
	{
		if(j == -1 || p[i] == p[j])
		{
			i++;
			j++;
	        next[i] = j;
		}
		else
		   j = next[j];
	}
}

int main()
{
    int n, m;
    while(~scanf("%s%s",str1, str2))
    {
        int len1 = strlen(str1);
        int len2 = strlen(str2);
        strcat(str1, str2);
        
        get_next(str1);
        int len = len1 + len2;
        
        while(next[len] > len1 || next[len] > len2)  // 着重理解一下
            len = next[len];
            
        if(next[len])
        {
            for(int i = 0; i < next[len]; i ++)
                printf("%c",str1[i]);
            printf(" %d\n",next[len]);
        }
        else
            printf("0\n");
    }
    return 0;
}

X - 最长回文

Description

给出一个只由小写英文字符a,b,c...y,z组成的字符串S,求S中最长回文串的长度. 
回文就是正反读都是一样的字符串,如aba, abba等
 

Input

输入有多组case,不超过120组,每组输入为一行小写英文字符a,b,c...y,z组成的字符串S 
两组case之间由空行隔开(该空行不用处理) 
字符串长度len <= 110000
 

Output

每一行一个整数x,对应一组case,表示该组case的字符串中所包含的最长回文长度. 
 

Sample Input

aaaa abab
 

Sample Output

4 3

    manacher 的模版体,具体可以参见http://blog.csdn.net/xi__long/article/details/43939067

    

#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <string.h>
#include <math.h>
using namespace std;

#define N 110010

char str[N];
char stc[N << 1];
int slen[N << 1];

void init(char *st, int len)
{
    stc[0] = '@';
    for(int i = 1; i <= 2*len; i += 2)
    {
        stc[i] = '#';
        stc[i + 1] = str[i/2];
    }
    stc[2*len + 1] = '#';
    stc[2*len + 2] = '$';
    stc[2*len + 3] = 0;
}

int manacher(char *st, int len)
{
    int mx = 0, ans = 0, po = 0;
    for(int i = 1; i <= len; i ++)
    {
        if(mx > i)
            slen[i] = min(mx - i, slen[2*po - i]);
        else
            slen[i] = 1;
        while(stc[i - slen[i]] == stc[i + slen[i]])
            slen[i] ++;

        if(slen[i] + i > mx)
        {
            mx = slen[i] + i;
            po = i;
        }
        ans = max(ans, slen[i]);
    }
    return ans - 1;
}

int main()
{
    while(~scanf("%s",str))
    {
        int lenth = strlen(str);
        init(str, lenth);
        printf("%d\n",manacher(stc, lenth * 2 + 1));
    }
}

抱歉!评论已关闭.