标签:
觉得这题挺有意思,看了别的博客,找到了一种目前看来还不错的算法,为强化理解,就写了下来。
题目意思:
实现一个挺高级的字符匹配算法:
给一串字符串,要求找到符合要求的字符串,例如对于目的串:123,那么给定字符串中诸如1******3*****2,12******3这些形式的子串都要找出来,即子串中含有目的串的所有字符,输出所有符合条件的字符串,并求出最短子串 。类似于和谐系统。
例如:假如目的串为:"423",输入长字符串为:"4fsdfk2jfl3fd2jfksd3j4d4d4jkfd4jd3kdf2",则应该输出"4fsdfk2jfl3","2jfksd3jld4","4jd3kdf2".同时再输出长度最小的字符串。
基本思路:
用两个变量 front,rear 指向一个的子串区间的头和尾(当然,开始时front和rear都指向字符串开始处)。 用一个int cnt[255]={0}记录当前这个子串里字符集a,b,c各自的个数,一个变量count记录字符集里有多少个。rear 一直加,更新cnt[]和count的值,直到count等于字符集个数。然后front++,直到cnt[]里某个字符个数为0(front 开始的部分有可能和后面的重复,所以front要加到某个字符个数为0,即
该字符在后面不会有重复),这样就找到一个符合条件的字串了,继续下去,可以求出所有符合条件的串,同时可以求出满足条件最短子串。
实现算法的要点:(结合代码说明)
1)区分匹配字符与普通字符。
这里使用一个哈希表来记录匹配字符。当然因为字符的范围是0-255,因此使用一个长度为256的int型数组就可以表示匹配字符的集合了。当然,为节省空间,使用bitset也可以实现相同的效果。
2)扫描字符串,一旦扫描到匹配字符,则执行下列操作:
1、该字符从未扫描过,则count++,表示扫描到的匹配字符数count=+1,同时该字符的个数cnt[char]=+1
2、该字符已经扫描到过,则该字符个数cnt[char]=+1
3)一旦扫描到的匹配字符数count等于字符集的个数,那么就产生了一个可匹配的字符串,需要从中提取出最短的子串,提取的方法是这里的重点,提取方法如下:
1、从front开始扫描字符,若扫描到非字符集内的字符,则继续扫描;若扫描到字符集内字符,则根据其字符的出现次数来决定下一步的操作。
2、若该字符集字符仅出现一次,那么该字符就是子串的起始字符,直接输出,完成字符的提取操作。
3、若该字符出现多次,说明从此处出发得到的子串不可能是最短子串,将该字符的出现次数cnt[char]-1,同时front++,继续向后扫描,重复步骤1、。画图说明此步骤的原因:
4)确定了一个可能最短子串后,需要继续向后扫描,front需要向后移动,由于front移动前指向的字符只出现了一次,移动后,匹配字符数count-1。画图说明
以上是算法中的关键点,下面给出代码:
#include <iostream> #include <cstring> using namespace std; void MinSubString( char *src, char *des ) { int min=1000;//找最短子串 int minfront=0;//最短子串开始位置 int minrear=0;//最短子串结束位置 int front,rear; front=rear=0; int len=strlen(des); int hashtable[255]={0}; int cnt[255]={0}; for(int i=0; i<len; i++)//将字符集里的字符映射到hashtable数组中,方便判断src中的某个字符是否在字符集中 hashtable[*(des+i)]=1; int count=0; char *p=src; while( *(p+rear) !=‘\0‘) { if(hashtable[*(p+rear)]==1)//rear当前字符在字符集中 { if(cnt[*(p+rear)]==0)//判断是否是本子串中第一次检索到此字符,由count统计字符集中已出现的字符数 { count++; cnt[*(p+rear)]++; if(count == len)//字符集中的字符在本子串中都已检索到 { //从front至rear的范围内提取最短的子串 while(1) { if(hashtable[*(p+front)]==1)//front当前字符在字符集中 { //第一张图体现了提取的原理 cnt[*(p+front)]--; if(cnt[*(p+front)]==0)//字符集中某个字符为0,此时front到rear所指字符串即为符合条件的子串 { for(int i=front; i<=rear; i++)//打印此子串 cout<<*(p+i); cout<<endl; if(rear-front+1<min) { min=rear-front+1; minrear=rear; minfront=front; } //开始一个新的子串,从front后面的字符开始一个新的子串 //front为字符集内的字符,front++后,就少了一个字符,因此 //要count--. 第二张图反映了这个过程 count--; front++; break; } } front++; } } } else cnt[*(p+rear)]++; } //当前字符不在字符集中 rear++; } cout<<"最短子串:"; for(int i=minfront ; i<=minrear; i++) cout<<*(p+i); cout<<endl; } int main() { //char *src="ab1dkj2ksjf3ae32ks1iji2sk1ksl3ab;iksaj1223"; //char *src="2sk1ksl3ab;iksaj1223"; //char *src="ab1dkj2ksjf3ae32ks1iji2sk1ksl3ab;1ik3saj123"; char *src="adhe1jk2jk2jkj1jk2jksd2mjkl3jk1kj4lkkj"; char *des="1234"; MinSubString( src, des ); return 0; }
参考文章:
http://blog.csdn.net/cxllyg/article/details/7595878,博主提供的思路挺好的,leonlovezh在评论中给出了改进意见,觉得改进的想法更简洁,本文中采纳该想法。
http://www.cnblogs.com/tractorman/p/4064054.html
标签:
原文地址:http://www.cnblogs.com/asdfping/p/4299035.html