hihoCoder#1015_KMP算法
2016-03-31 11:07
295 查看
题目:KMP算法
我的ac代码:
#include <iostream>
#include <string>
void GetNext(const std::string T, int *& next)
{
int i = 0;
int j = -1;
int len = T.length();
next[0] = -1;
while (i < len)
{
if (j == -1 || T[i] == T[j])
next[++i] = ++j;
else
j = next[j];
}
}
int KMP(const std::string& S, const std::string& T, int *& next)
{
int i = 0, j = 0;
int sum = 0;
while (i < S.size())
{
if (j == -1 || S[i] == T[j])
{
if (j == T.size() - 1)
{
++sum;
j = next[j];
}
else
{
++i;
++j;
}
}
else
j = next[j];
}
return sum;
}
int main()
{
int N;
std::string S, T;
std::cin >> N;
while (N--)
{
std::cin >> T >> S;
int *next=new int [T.size()+1];
GetNext(T, next);
std::cout << KMP(S, T, next) << std::endl;
T.clear();
S.clear();
delete []next;
next = NULL;
}
return 0;
}
之前对KMP算法不是很了解,看了一些资料后感觉理解了一点,下面自己写下自己对KMP的认识吧。
看了很多资料,真的是乱,有些数组从0开始,有些从1开始,看的头疼,这里是从0开始的。
我觉得,KMP算法主要是next数组的理解,我们先假设主串为:
,模式串为
。那么,当主串和模式串失配时,假设失配位置为
和
,即
,此时模式串向右滑动的可行距离有多远,也就是说,当主串第
个字符和模式串第
个字符失配时,主串第
(
不会回溯)个字符应该和模式串哪个字符比较?
我们假设此时主串第
个字符应该与模式串的第
个字符继续比较,则模式串前
个字符必有下列关系式:
(1)
又因为失配位置是在
和
(
),也就是说,模式串第
位置前面的字符和主串是匹配的,即:
(2)
(2)式中,我们如果只取“部分”,即取k个字符,,则:
(3)
那么,由式(1)和(3),可以得到:
(4)
如果令
,那么,这个
的含义就是式(4)。
并且,由k的用途我们知道,
的意思就是,模式串中第
个字符不匹配,就用模式串中的第
个字符和主串比较。
那么,如何求得next数组?
首先,由于我们的字符串下标从0开始,所以:
,
然后,我们设
,由
的含义(4)知,模式串存在如下关系:
(就是式(4)),
那么,此时
如何计算?我们分情况讨论:
(1)
则表明模式串中存在如下关系:
(5)
根据
和式(4)的关系可知,由式(5)可以得到:
(2)
我们将模式串看成一个主串,”另一个模式串“作为模式串,那么在模式串的第k位出现不匹配,所以,根据next数组的作用,应该用模式串的第
个字符和主串中的
比较,我们令
,如果
,那么有:
所以就有:
,也就是:
同样,如果
,继续next,直至
在模式串中找到匹配的字符,或者,“找到头”也找不到匹配的字符。
如果“找到头”也找不到匹配的字符,则:
下面,举个例子:
我们已经求得next[6]=3,现在要求next[7]。由next[6]=3得:
又
所以:
所以:
next[7]=4
我们继续求next[8],,因为next[7]=4有
,但是
,所以下一个和“主串”中
比较的字符是第
,即第1(注意,下标从0开始)个字符,此时:
,继续next,
,所以下一个和“主串”中
比较的字符是第0个字符,有
,所以,
。
因此,我们可以得到求next数组的代码:
我们注意到,j=-1和T[i] == T[j]内代码实现的操作是一样的(j=-1时,j+1刚好就是0),所以可以合并在一起:
再简洁一点可以写成:
我的ac代码:
#include <iostream>
#include <string>
void GetNext(const std::string T, int *& next)
{
int i = 0;
int j = -1;
int len = T.length();
next[0] = -1;
while (i < len)
{
if (j == -1 || T[i] == T[j])
next[++i] = ++j;
else
j = next[j];
}
}
int KMP(const std::string& S, const std::string& T, int *& next)
{
int i = 0, j = 0;
int sum = 0;
while (i < S.size())
{
if (j == -1 || S[i] == T[j])
{
if (j == T.size() - 1)
{
++sum;
j = next[j];
}
else
{
++i;
++j;
}
}
else
j = next[j];
}
return sum;
}
int main()
{
int N;
std::string S, T;
std::cin >> N;
while (N--)
{
std::cin >> T >> S;
int *next=new int [T.size()+1];
GetNext(T, next);
std::cout << KMP(S, T, next) << std::endl;
T.clear();
S.clear();
delete []next;
next = NULL;
}
return 0;
}
之前对KMP算法不是很了解,看了一些资料后感觉理解了一点,下面自己写下自己对KMP的认识吧。
看了很多资料,真的是乱,有些数组从0开始,有些从1开始,看的头疼,这里是从0开始的。
我觉得,KMP算法主要是next数组的理解,我们先假设主串为:
,模式串为
。那么,当主串和模式串失配时,假设失配位置为
和
,即
,此时模式串向右滑动的可行距离有多远,也就是说,当主串第
个字符和模式串第
个字符失配时,主串第
(
不会回溯)个字符应该和模式串哪个字符比较?
我们假设此时主串第
个字符应该与模式串的第
个字符继续比较,则模式串前
个字符必有下列关系式:
(1)
又因为失配位置是在
和
(
),也就是说,模式串第
位置前面的字符和主串是匹配的,即:
(2)
(2)式中,我们如果只取“部分”,即取k个字符,,则:
(3)
那么,由式(1)和(3),可以得到:
(4)
如果令
,那么,这个
的含义就是式(4)。
并且,由k的用途我们知道,
的意思就是,模式串中第
个字符不匹配,就用模式串中的第
个字符和主串比较。
那么,如何求得next数组?
首先,由于我们的字符串下标从0开始,所以:
,
然后,我们设
,由
的含义(4)知,模式串存在如下关系:
(就是式(4)),
那么,此时
如何计算?我们分情况讨论:
(1)
则表明模式串中存在如下关系:
(5)
根据
和式(4)的关系可知,由式(5)可以得到:
(2)
我们将模式串看成一个主串,”另一个模式串“作为模式串,那么在模式串的第k位出现不匹配,所以,根据next数组的作用,应该用模式串的第
个字符和主串中的
比较,我们令
,如果
,那么有:
所以就有:
,也就是:
同样,如果
,继续next,直至
在模式串中找到匹配的字符,或者,“找到头”也找不到匹配的字符。
如果“找到头”也找不到匹配的字符,则:
下面,举个例子:
我们已经求得next[6]=3,现在要求next[7]。由next[6]=3得:
又
所以:
所以:
next[7]=4
我们继续求next[8],,因为next[7]=4有
,但是
,所以下一个和“主串”中
比较的字符是第
,即第1(注意,下标从0开始)个字符,此时:
,继续next,
,所以下一个和“主串”中
比较的字符是第0个字符,有
,所以,
。
因此,我们可以得到求next数组的代码:
void Next(std::string &T,int next[]) { int len = T.length(); next[0] = -1; int i = 0; int j = -1; while (i < len) { if (j == -1) { next[i + 1] = 0; i++; j++; } else if (T[i] == T[j]) { next[i + 1] = j + 1; i++; j++; } else j = next[j]; } }
我们注意到,j=-1和T[i] == T[j]内代码实现的操作是一样的(j=-1时,j+1刚好就是0),所以可以合并在一起:
void Next(std::string &T,int next[]) { int len = T.length(); next[0] = -1; int i = 0; int j = -1; while (i < len) { if (j == -1 || T[i] == T[j]) { next[i + 1] = j+1;//这里不能写next[i]+1,因为j可能是经过next[j]得到的 i++; j++; } else j = next[j]; } }
再简洁一点可以写成:
void Next(std::string &T,int next[]) { int len = T.length(); int i = 0; int j = next[0] = -1; while (i < len) { if (j == -1 || T[i] == T[j]) next[++i] = ++j; else j = next[j]; } }
相关文章推荐
- Java设计模式——桥模式
- Android studio的gradle目录结构
- TCP/IP协议族的四个层次及不同层次的协议
- Java 命名规范
- 怎么实现字符串的反转,如:输入abc,输出cba
- 用HTML+CSS实现简单的下拉列表
- java 异常处理
- javascript笔记——jsonp
- 程序解答
- AdjustTokenPrivileges启用权限
- Android系统开发之四:多线程和Synchronized同步机制
- Linux中Python 环境软件包安装步骤
- spring-jdbc-RoutingDataSource
- JNI调用机制
- 各种jar包下载
- 心电图效果View Android自定义View
- MongoDB学习入门相关资料索引
- Ajax库的编写及使用
- 052(五)
- tableView又遇到高亮选中状态混乱的问题了