一步一步写算法(之字符串查找 下篇)

原贴地址:http://blog.csdn.net/feixiaoxing/article/details/6915588

【 声明:版权所有,欢迎转载,请勿用于商业用途。  联系信箱:feixiaoxing @163.com】


    前面我们谈到了KMP算法,但是讲的还不是很详细。今天我们可以把这个问题讲的稍微详细一点。假设在字符串A中寻找字符串B,其中字符串B的长度为n,字符串A的长度远大于n,在此我们先忽略。

    假设现在开始在字符串A中查找,并且假设双方在第p个字符的时候发现查找出错了,也就是下面的情况:

[cpp]  view plain copy
  1. /*       
  2. *    A: A1 A2 A3 A4 ... Ap ............ 
  3. *    B: B1 B2 B3 B4 ... Bp ...Bn  
  4. *                       (p)         
  5. */      

    那么这时候,A有什么选择呢?它可以左移1位,用A2~A(p-1)比较B1~B(p-2),然后再用A(p)~A(n+1)比较B(p-1)~B(n)位;或者左移2位,用A3~A(p-1)比较B1~B(p-3),然后再用A(p)~A(n+2)比较B(p-2)~B(n)位; 依次类推,直到左移(p-2)位,用A(p-1)比较B(1),然后再用A(p)~A(p+n-2)比较B(2)~B(n)位。

    不知道细心的朋友们发现什么规律没?因为A和B在前面(p-1)个数据是相等的,所以上面的计算其实可以这样看:用A2~A(p-1)比较B1~B(p-2),实际上就是B2~B(p-1)比较B1~B(p-2); 用A3~A(p-1)比较B1~B(p-3),实际上就是B3~B(p-1)比较B1~B(p-3);最后直到B(p)和B(1)两者相比较。既然这些数据都是B自身的数据,所以当然我们可以提前把这些结果都算出来的。

    那么这么多的选择,我们应该左移多少位呢?

    其实判断很简单。假设我们左移1位,发现A2~A(p-1)的结果和B1~B(p-2)是一致的,那么两者可以直接从第(p-1)位开始比较了; 如果不成功呢,那么只能左移2位,并判断A2~A(p-1)和B1~B(p-2)的比较结果了,......,这样以此类推进行比较。如果不幸发现所有的数据都不能比较成功呢,那么只能从头再来,从第1位数据依次进行比较了。

    不知道讲清楚了没,还没有明白的朋友可以看看下面的代码:

[cpp]  view plain copy
  1. int calculate_for_special_index(char str[], int index)  
  2. {  
  3.     int loop;  
  4.     int value;  
  5.       
  6.     value = 0;  
  7.     for(loop = 1; loop < index; loop ++){  
  8.         if(!strncmp(&str[loop], str, (index - loop))){  
  9.             value = index - loop;  
  10.             break;  
  11.         }  
  12.     }  
  13.       
  14.     return (value == 0) ? 1 : (index - value);  
  15. }  
  16.   
  17. void calculate_for_max_positon(char str[], int len, int data[])  
  18. {  
  19.     int index;  
  20.       
  21.     for(index = 0; index < len; index++)  
  22.         data[index] = calculate_for_special_index(str, index);  
  23. }  

    当然,上面当然都是为了计算在索引n比较失败的时候,判断此时字符应该向左移动多少位。

[cpp]  view plain copy
  1. char* strstr_kmp(const char* str, char* data)  
  2. {  
  3.     int index;  
  4.     int len;  
  5.     int value;  
  6.     int* pData;  
  7.   
  8.     if(NULL == str || NULL == str)  
  9.         return NULL;  
  10.   
  11.     len = strlen(data);  
  12.     pData = (int*)malloc(len * sizeof(int));  
  13.     memset(pData, 0, len * sizeof(int));  
  14.     calculate_for_max_positon((char*)str, len, pData);  
  15.   
  16.     index = 0;  
  17.     while(*str && ((int)strlen(str) >= len)){  
  18.         for(; index < len; index ++){  
  19.             if(str[index] != data[index])  
  20.                 break;  
  21.         }  
  22.           
  23.         if(index == len){  
  24.             free(pData);  
  25.             return (char*) str;  
  26.         }  
  27.       
  28.         value = pData[index];  
  29.         str += value;  
  30.   
  31.         if(value == 1)  
  32.             index = 0;  
  33.         else  
  34.             index = index -value;  
  35.     }  
  36.       
  37.     free(pData);  
  38.     return NULL;  
  39. }  

    可能朋友们看到了,上面的strlen又回来了?说明代码本身还有优化的空间。大家可以自己先试一试。

[cpp]  view plain copy
  1. int check_valid_for_kmp(char str[], int start, int len)  
  2. {  
  3.     int index;  
  4.   
  5.     for(index = start; index < len; index++)  
  6.         if('\0' == str[index])  
  7.             return 0;  
  8.     return 1;  
  9. }  
  10.   
  11. char* strstr_kmp(const char* str, char* data)  
  12. {  
  13.     int index;  
  14.     int len;  
  15.     int value;  
  16.     int* pData;  
  17.   
  18.     if(NULL == str || NULL == str)  
  19.         return NULL;  
  20.   
  21.     len = strlen(data);  
  22.     pData = (int*)malloc(len * sizeof(int));  
  23.     memset(pData, 0, len * sizeof(int));  
  24.     calculate_for_max_positon((char*)str, len, pData);  
  25.   
  26.     index = 0;  
  27.     while(*str && check_valid_for_kmp((char*)str, index, len)){  
  28.         for(; index < len; index ++){  
  29.             if(str[index] != data[index])  
  30.                 break;  
  31.         }  
  32.           
  33.         if(index == len){  
  34.             free(pData);  
  35.             return (char*) str;  
  36.         }  
  37.       
  38.         value = pData[index];  
  39.         str += value;  
  40.   
  41.         if(value == 1)  
  42.             index = 0;  
  43.         else  
  44.             index = index -value;  
  45.     }  
  46.       
  47.     free(pData);  
  48.     return NULL;  
  49. }  

(三)、多核查找

    多核查找其实不新鲜,就是把查找分成多份,不同的查找过程在不同的核上面完成。举例来说,我们现在使用的cpu一般是双核cpu,那么我们可以把待查找的字符分成两份,这样两份查找就可以分别在两个核上面同时进行了。具体怎么做呢,其实不复杂。首先我们要定义一个数据结构:

[cpp]  view plain copy
  1. typedef struct _STRING_PART  
  2. {  
  3.     char * str;  
  4.     int len;  
  5. }STRING_PART;  
    接着,我们要做的就是把字符串分成两等分,分别运算起始地址和长度。

[cpp]  view plain copy
  1. void set_value_for_string_part(char str[], int len, STRING_PART part[])  
  2. {  
  3.     char* middle = str + (len >> 1);  
  4.   
  5.     while(' ' != *middle)  
  6.         middle --;  
  7.   
  8.     part[0].str = str;  
  9.     part[0].len = middle - (str -1);  
  10.   
  11.     part[1].str = middle + 1;  
  12.     part[1].len = len - (middle - (str - 1));  
  13. }  
    分好之后,就可以开始并行运算了。

[cpp]  view plain copy
  1. char* strstr_omp(char str[], char data[])  
  2. {  
  3.     int index;  
  4.     STRING_PART part[2] = {0};  
  5.     char* result[2] = {0};  
  6.     int len = strlen(str);  
  7.   
  8.     set_value_for_string_part(str, len, part);  
  9.   
  10. #pragma omp parellel for   
  11.     for(index = 0; index < 2; index ++)  
  12.         result[index] = strstr(part[index].str, part[index].len, data);  
  13.   
  14.     if(NULL == result[0] && NULL == result[1])  
  15.         return NULL;  
  16.   
  17.     return (NULL != result[0]) ? result[0] : result[1];  
  18. }  
注意事项:

    (1)这里omp宏要在VS2005或者更高的版本上面才能运行,同时需要添加头文件#include<omp.h>,打开openmp的开关;

    (2)这里调用的strstr函数第2个参数是目标字符串的长度,和我们前面介绍的普通查找函数稍微不一样,前面的函数不能直接使用,但稍作改变即可。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值