HDU-3363 Count the string KMP 巧用next函数

HDU-3363 Count the string KMP 巧用next函数

  正如题目所说,该题正是巧用next函数求得的,题目意思:给定一个串,求以它自身长度为(1,2,3…… N)的子串作为模式串,以完整的自身作为母串,求最后所得到的总匹配数。  其中样例中的 abab 结果是 6( a 出现两次, ab出现两次, abc出现一次, abcd出现一次 ), aaaa的输出结果应该为 10 ( a出现四次, aa出现三次, aaa出现两次, aaaa出现一次 )。

  还记得前面三道KMP题,一道(1711)是搜索返回是否成功匹配,一道(2087)是搜索模式串出现多少次(母串不可重复计算), 最后一道(1686)同样是搜索模式串出现多少次(当然母串可以重复计算),难度是依次递增。这次这道题目也算是在第三题的基础上演变而来,本来以为这一题与1686差不多,我找出所有第一个字母出现的下表,收集为一个索引表,然后再进行KMP匹配,每次只要匹配到一个字母那么以这个字母的前缀匹配数就加上一。数据是过了,但也只有在超时时才追悔莫及。

  其实在写KMP的时候就觉得很像getnext(  )函数,因为我正在做的事情就是将自己与本身串进行比较。最终的结果说明,这里只需运行一个getnext(  )函数就能够将此题解决,因为这很好了贴切了这个题目的意思,将自身与自身进行匹配。

  现就串 ababababa 进行拆解:

  1. 首先运行getnext(  )函数,我们将会得到这个串的 next[] 表,这里可以得到 next[] 表:

      A.  011234567   

     2. 再对next[]表的意义做进一步分析,首先我们应该明确所有的字符对应next[]值均是由上一个字符匹配的结果所得到的,这也解释了为什么我们要单独将next[1]赋为0,要知道这时候我们的匹配工作还没有开始。设k= next[i], k的值的意思是说,到 i- 1 号 字符,并且以 i- 1 号为终点,以某一点为起点,与本身从 1 号字符开始成功且最大可能的与本身匹配了k个字符(方向均为从左到右)。即满足  str[ 1, 2, 3…… k- 1 ]= str[ i- k+ 1, i- k+ 2, i- k+ 3…… i- 1 ],由于在求next的过程中,每次匹配成功我们并没有回溯模式串的指针,因此这样能够保证每个next[]的值均是最大的。回到问题上来,看如何利用next[]值来解决这道题目,第一个字符next[]值无意义,因为首字符的前面谈不上匹配成功。从第二个字符开始,A串的意思: (注意橙色字体) 可以转化为 001234567 这就是还原了之后的匹配值( 处理时就是在 自增之前进行处理 ),即每一位表示到当前位置时,最多匹配了多少字符。

  3. 最后分析如何计算前缀匹配总值,这里我们需要重新开辟一个数组来记录到达每一字符时会新出现多少个前缀匹配,用rec[]表示,初始化rec[1]= 1; 这表示到第一个字符时,就与自身匹配构成1种情况。 已知第5个字符的匹配值为3,那么到达这个字符时的那么新出现的匹配前缀应该为 rec[3]+ 1, 前面的rec[3]是因为这里出现了前三个字符的重复段,后面的 1 是因为 str[1-5] 本身的一个前缀串。 理清了这些关系,代码就可以写下来了。

  代码如下:

#include <iostream>
#include <cstring>

using namespace std;

char mix[200005];   /*_*/   int next[200005], rec[200005];

void getnext( char *s, int *n )
{
	int k= 1, j= 0, len= strlen( s+ 1 );
	next[1]= 0, rec[1]= 1;
	while( k<= len )
	{
		if( j== 0|| s[k]== s[j] )
		{
		    rec[k]=( rec[j] + 1 )% 10007;
			++k, ++j;
		    n[k]= j;
		}
		else
		{
			j= n[j];
		}
	}
}

int main(  )
{
	int T, N;
	cin>> T;
	while( T--&& cin>> N )
	{
		memset( rec, 0, sizeof( rec ) );
		cin>> mix+ 1;
		int len= strlen( mix+ 1 ), ans= 0;
		getnext( mix, next );
		for( int i= 1; i<= len; ++i )
		{
			ans+= rec[i]% 10007;
		} 
		ans %= 10007;
		cout<< ans<< endl;
	}
}

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/110584.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • sklearn库的使用_导入turtle库的方法

    sklearn库的使用_导入turtle库的方法Sklearn库是基于Python的第三方库,它包括机器学习开发的各个方面。机器学习的开发基本分为六个步骤,1)获取数据,2)数据处理,3)特征工程,4)机器学习的算法训练(设计模型),5)模型评估,6)应用。机器学习的算法一般分为两种:一种既有目标值又有特征值的算法称之为监督学习,另一种只有特征值的算法称之为无监督学习。而监督学习还可以继续细分为分类算法和回归算法。1)获取数据⑤Sklearn中获取数据集使用的包为Sklearn.datasets,之后可以接load_*和fetch_*从Skle

    2022年10月7日
    0
  • 垂死挣扎还是涅槃重生 — Delphi XE5 公布会归来感想

    垂死挣扎还是涅槃重生 — Delphi XE5 公布会归来感想

    2021年11月30日
    59
  • Java判断回文字符串_java将字符串反转输出

    Java判断回文字符串_java将字符串反转输出java判断回文字符串几种简单的实现:1.将字符串倒置后逐一比较,实现如下:publicclassHuiWenTest{/***@SERLIN*/publicstaticvoidmain(String[]args){Stringstr=””;System.out.println(“请输入一个字符串”);Scannerin

    2022年5月3日
    45
  • Oracle ASMM和AMM

    Oracle ASMM和AMMASMM(AutomaticSharedMemoryManagement,自动共享内存管理)是Oracle10g引入的概念。通过使用ASMM,就不需要手工设置相关内存组件的大小,而只为SGA设置一个总的大小,Oracle的MMAN进程(MemoryManagerProcess,内存管理进程)会随着时间推移,根据系统负载的变化和内存需要,自动调整SGA中各个组件的内存大小。ASM…

    2022年6月7日
    48
  • OkhttpClient的使用详解

    **概述及特性**HTTP是现代应用常用的一种交换数据和媒体的网络方式,高效地使用HTTP能让资源加载更快,节省带宽。OkHttpClient是一个高效的HTTP客户端,它有以下默认特性:支持HTTP/2,允许所有同一个主机地址的请求共享同一个socket连接连接池减少请求延时透明的GZIP压缩减少响应数据的大小缓存响应内容,避免一些完全重复的请求当网络出现问题的时候OkHttp依…

    2022年4月1日
    40
  • 2020微信小程序反编译教程(小程序反编译源码能用吗)

    文章主要实现:废话不多说下面就直接来流程了!第1步:先安装node.js点击下载第2步:再下载wxappUnpacker反编译包点击下载包第3步:保证以上都安装后电脑命令窗口:CMD运行第2步目录运行加载node依赖:命令窗口复制以下黄色命令:npminstalluglify-es–savenpminstall…

    2022年4月16日
    366

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号