微软面试100题:字符串匹配算法,查找包含字符集的子串

觉得这题挺有意思,看了别的博客,找到了一种目前看来还不错的算法,为强化理解,就写了下来。

题目意思:

实现一个挺高级的字符匹配算法:

给一串字符串,要求找到符合要求的字符串,例如对于目的串: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、。画图说明此步骤的原因:

   image

 

4)确定了一个可能最短子串后,需要继续向后扫描,front需要向后移动,由于front移动前指向的字符只出现了一次,移动后,匹配字符数count-1。画图说明

image

以上是算法中的关键点,下面给出代码:

#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

posted @ 2015-02-24 23:10  asdfping  阅读(674)  评论(0编辑  收藏  举报