您的位置:首页 > 编程语言 > C语言/C++

【POJ 3461】Oulipo 中文题意&题解&代码(C++)

2016-02-16 20:23 519 查看
**

Oulipo

**

Time Limit: 1000MS Memory Limit: 65536K

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

中文题意:

简单说,就是给你n组数据,每组数据中包含两个字符串s和t,找出字符串s在字符串t中出现的次数,例:ababa中字符串aba出现2次,输出2。

题解:

了解kmp之后可以一眼就看出这是一道kmp的模板题,这里谈一下kmp算法的个人理解。

kmp算法是一种用于两个字符串直接匹配的效率较高的算法,它的核心思想是构造一个fail指针(本文中建立一个nex数组来表示),当字符串匹配到某一位失败时,就找到指针所指的地方开始重新匹配,而不是重头开始匹配。

举例: 要在 a b a c a a b a c a b a c a b a a b b 中匹配 a b a c a b。

假设将 a b a c a a b a c a b a c a b a a b b 称为串 s

……. 将 a b a c a b 称为串 t

a b a c a a b a c a b a c a b a a b b
a b a c a b

0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18
a  b  a  c  a  a  b  a  c  a  b  a  c  a  b  a  a  b  b
a  b  a  c  a  b


复杂的nm算法就是暴力枚举如下:

for (int i=0;i<s.size();i++)
{
int now=i,j=0;
while(s[now]==t[j] && j<=t.size())
j++,now++;
if (j==t.size()) cout<<"YES"<<endl,break;
}


我们可以看到这个算法的实质是首先将串 s 和 串 t 的第一位对齐进行匹配,若匹配成功则不再继续匹配,若匹配失败则将串 s 的下一位和串 t 的第一位对齐匹配,直到无法再匹配,这样的复杂度是很高的,至少本题这么做肯定是过不了。

那么该怎么办呢,是什么地方浪费了复杂度呢,以上述字符串为例,首先将两个字符串对齐,当第一次匹配到 t[5] 时发现匹配失败,此时我们发现如果直接将串 t 向右移一位匹配时,因为我们的for循环已经扫过了串s的前几位,发现这些地方不可能再和串t成功匹配,向右一位一位的移完全是浪费时间,我们完全可以直接将字符串 t[0] 向右平移到 s[5] 的位置进行匹配才有可能得到有效的答案,然而我们要怎样才能实现这样的操作呢,这里就要引入一个 nex[] 数组,即上文提到的fail指针。

nex[i]表示串 t 的 t[0]~t[ nex[i] ] 与串t的 t [ i - nex[i] ]~ t [ i ] 都相同

在本题中

0  1  2  3  4  5
t      a  b  a  c  a  b
nex   -1 -1  0 -1  0  1


那么这个 nex[] 数组该怎么求得呢,附代码解释:

很抱歉..博主本人写到这里已经快崩溃了QAQ,看来kmp的思想还是没有完全理解透,就不强行装x了,等真正理解了再继续写,所以大家还是去看别的大神的解释吧…orz

但是代码还是得附的,大家自己意会吧……

inline void getnex()
{
int j=-1;
for (int i=0;i<lent;i++)
{
while(t[i]!=t[j+1] && j!=-1) j=nex[j];
if (t[i]==t[j+1] && i!=0) j++;
nex[i]=j;
}
}


求nex是kmp的核心部分,当你理解了求nex的部分后下面的部分也就很容易看懂了

kmp:

inline void kmp()
{
int j=-1;
for (int i=0;i<lens;i++)
{
while(s[i]!=t[j+1] && j!=-1) j=nex[j];
if (s[i]==t[j+1]) j++;
if (j==lent-1) ans++,j=nex[j];
}
}


这道题的代码:

#include<iostream>
#include<algorithm>
#include<stdio.h>
#include<string.h>
using namespace std;
char s[1000005],t[10005];//
int nex[10005],ans,n,lens,lent;
inline void getnex() { int j=-1; for (int i=0;i<lent;i++) { while(t[i]!=t[j+1] && j!=-1) j=nex[j]; if (t[i]==t[j+1] && i!=0) j++; nex[i]=j; } }
inline void kmp() { int j=-1; for (int i=0;i<lens;i++) { while(s[i]!=t[j+1] && j!=-1) j=nex[j]; if (s[i]==t[j+1]) j++; if (j==lent-1) ans++,j=nex[j]; } }
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
ans=0;
scanf("%s %s",t,s);
lens=strlen(s);
lent=strlen(t);
getnex();
kmp();
printf("%d\n",ans);
}
}


感觉对那些写出来kmp算法详解的大神都好牛,虽然看他们写的我并不能看太懂,但是好歹也写出来了QAQ,看来水平真的很渣啊…
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: