.NET 配置文件简单使用

摘要:
设置XmlNodesection){varnode=section.ChildNodes[0];attr);IsRequired=true)]publicstringProvider{get{return(string)this[“provider”];}set{this[“provider”]=(object)value;
.NET 配置文件简单使用

   当我们开发系统的时候要把一部分设置提取到外部的时候,那么就要用到.NET的配置文件了。比如我的框架中使用哪个IOC容器需要可以灵活的选择,那我就需要把IOC容器的设置提取到配置文件中去配置。实现有几种方法。

1.使用appSettings

这个是最简单的可以设置和读取的用户设置

image

程序中可以用key去读取:

string objContainer = ConfigurationManager.AppSettings["objectContainer"];

简单实用但是不够优雅。

2.实现自己的配置节点

image

首先在configSections节点配置自己的配置解析类。

那么如何来解析这段配置呢?有两个办法。

方法1:

实现IConfigurationSectionHandler接口来自己解析配置文件的xml文件。

public class ObjectContainerElement
{
          public string Provider {get;set;}
          public string IocModule {get; set;}
}
 
public class AgileFRConfigurationHandler: IConfigurationSectionHandler
    {
        public object Create(object parent, object configContext, XmlNode section)
        {
               
               var node =section.ChildNodes[0];
                if (node.Name != "objectContainer")
                    throw new ConfigurationErrorsException("不可识别的配置项", node);
            var config = new ObjectContainerElement();
                foreach (XmlAttribute attr in node.Attributes)
                {
                    switch (attr.Name)
                    {
                        case "provider":
                            config. Provider = attr.Value;
                            break;
                        case "iocModule":
                            config .IocModule = attr.Value;
                            break;
                        default:
                            throw new ConfigurationErrorsException("不可识别的配置属性", attr);
                    }
                }
            }
            return config;
        }
 
//使用
 var config = ConfigurationManager.GetSection("agileFRConfiguration") as ObjectContainerElement;

这个方法看上去就略屌了,不过就是太麻烦了。

方法2:

继承ConfigurationSection类,配合ConfigurationProperty特性来实现

 public class ObjectContainerElement : ConfigurationElement
    {
        [ConfigurationProperty("provider", IsRequired = true)]
        public string Provider
        {
            get
            {
                return (string)this["provider"];
            }
            set
            {
                this["provider"] = (object)value;
            }
        }
        [ConfigurationProperty("iocModule", IsRequired = false)]
        public string IocModule
        {
            get
            {
                return (string)this["iocModule"];
            }
            set
            {
                this["iocModule"] = (object)value;
            }
        }
    }

 /// <summary>
    /// 配置处理类
    /// </summary>
    public class AgileFRConfigurationHandler : ConfigurationSection
    {
        [ConfigurationProperty("objectContainer", IsRequired = true)]
        public ObjectContainerElement ObjectContainer
        {
            get
            {
                return (ObjectContainerElement)this["objectContainer"];
            }
            set
            {
                this["objectContainer"] = (object)value;
            }
        }
    }
//使用
var configurationHandler = (AgileFRConfigurationHandler)ConfigurationManager.GetSection("agileFRConfiguration");
var objectContainer=configurationHandler.ObjectContainer;
 
这个方法简单优雅,我喜欢。
 

3.Settings.settings

这个方法我不太喜欢,它会自己生成配置文件对应的Class。不说了。
BTW:求 苏州,上海地区有激情,有意义的技术类工作!!

Email:kklldog@gmail.com 
作者:Agile.Zhou(kklldog) 
出处:http://www.cnblogs.com/kklldog/ 
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

 
  麻省理工公开课《算法导论》学习笔记:第一讲

主题:简介课程,渐近概念的大局观,插入排序和归并排序,递归式函数时间分析(递归树方法)

教材:《算法导论》

收获:很感动地看到算法分析那个log(n)是为什么出现了,更深层还要听第二讲,若不是因为要准备SAS,恨不得马上看。

内容:

1 何为算法分析?

      计算机程序运行性能和存储空间的理论分析,叫算法分析。也就是关注2点:1 性能,就是程序跑得快不快; 2 存储空间,即占用了多大的内存。但是主要还是关注性能。(可能是因为时间就是金钱吧,而且现在计算机硬件发展速度还不错)

 

2 比性能更加重要的因素都有哪些?

比如成本,正确性,功能特征(features),用户用好,模块化性等等。

 

3 那为何还学习算法和性能?

很妙的引入方法,你都说了上面那些东西比性能还重要,那尼玛干嘛还要学习算法?

学习算法的原因:

A performance measures the line between the feasible and the infeasible.

对于很多比性能更重要的因素,其实他们跟性能是密切相关的,比如用户用好,就需要你的程序响应时间控制在一定范围内。算法总是处在解决问题的最前沿。

B algorithms give us a language for talking about program behavior.

算法给出了程序行为的描述。

其实形象的比喻就是:水,食物这些东西都比钱重要,你为什么还需要钱?算法就像经济社会中的“货币”,它给出了衡量的一般标准。比如你付出了性能来追求安全稳健性,用户友好性等等。

比如JAVA程序一直很流行,其实它的性能比起C语言要差3倍多,但是由于它本身的面向对象,异常机制等原因,使得人们还是很愿意牺牲性能来追求可拓展性,安全性这些东东。个人的理解就是,比如在软件设计中,你要着重追求某些方面,那在哪种程度范围内是可以接受的呢?性能就是一个标准,比如牺牲3倍性能可以接受,10倍就不行。

C tons of fun

追求速度总是很有趣的,所以人们才会热衷于赛车,火箭等东西。

 

4 引入插入排序

插入排序(Insertion Sort)

4.1 描述

输入 A[1…n],要输出 A’[1,…n],使得A’中的元素按升序排序,也就是a’(1)<=a’(2)<=a’(3)…

4.2 伪代码:

For j=2 to n

{

  Do key=a[j]

     i=j-1;

While i>0 and a[i]>key

Do a[i+1]=a[i]

     I=i-1;

}

  A[i+1]=key

}

4.3 举例:

1 7 3 6 4 20 13 9

T1:看第二个元素,7,比1大,放在第二位

T2:看第三个元素,3,比7小,把7放到第三位,3再跟1 比较,比1大,所以3放在第二位,结果1 3 7

T3:类似,分析6,结果1 3 6 7

T4..

T7:1 3 4 6 7 9 13 20

4.4 本质:

每次循环后前面已经排好序的部分保持不变(这里的不变应该是指不用管了,而不是位置绝对不变),每次循环的目的是完成增量,使得已经排序的长度+1

 

5 通过插入排序,看运行时间依赖哪些因素,引入算法分析的渐近分析符号

5.1 运行时间依赖因素

A 输入的数列,如果已经排好序,工作量近似为0,而如果是逆序的,工作量最大

B 输入的数列规模n。6个数VS 6亿个数,所以后续的运行时间分析时,我们把时间看成是n的函数

5.2 T(n)引入

一般我们都想知道运行时间的下届,就是最差的情况,这样子对用户是一个保证,你可以跟人家说这个程序最多不超过3s,但是你不会说最少要3s吧,天知道最好3s,最差要多久,说不定是一辈子。

所以在算法分析中,我们一般是做最坏情况分析,可以用T(n)表示最长的运行时间,

T(n)is the max time on any input size n.

有时候我们也讨论平均情况。此时T(n) is the expected time over all input size n.也就是T(N)表示期望时间。

(什么是期望时间,数学上的公式是每种输入运行时间*每种输入出现的概率,通俗来讲就是加权平均。)

那么每种输入出现的概率是多大呢?我们假设一种统计分布:均匀分布,当然也可以选择其他分布。均匀分布,也就是说,每种输入出现的概率是一样的。

最好情况分析,这个是假象,一般是拿来忽悠人的。没什么用。

运行时间依赖于具体的机器,所以一般分析的时候是考虑相对速度,也就是在同一台机器上的运行速度。(相反的就是绝对速度,也就是在不同机器上做比较)

 

6 算法的大局观:The Big idea of algorithm——渐近分析

6.1 基本思路是:

忽略掉那些依赖于机器的常量(比如某条具体操作运行时间,如赋值操作),而且,不是去检查实际运行时间,而是关注运行时间的增长。什么意思呢?就是关注的是当输入规模n——》无穷时,T(n)是什么情况,而不是说n=10的时候的详细比较。

6.2 具体

渐近符号:Θ(n)

运算法则:对于一个公式,弃去它的低阶项,并忽略前面的常数因子。比如

Θ(3n^3+90n^2-5n+69)=Θ(n^3)

简单吧!

其实Θ(n)是有严格的数学定义的(下节课再一起讲),所以算法导论这门课既是讲数学,也是讲计算机科学的。

6.3 数学与工程的trade-offs

.NET 配置文件简单使用第3张

从图中(这个图画得不是一般的丑)可以看出,当n0趋于无穷时,Θ(n2)<<Θ(n3)

有时候交点n0太大,计算机无法运行,所以我们有时会对一些相对低速算法感兴趣。应该就是理论与实际的妥协trade-offs。

 

7 插入排序运行时间分析

 插入排序最坏情况:

T(n)=2+3+…+n=(2+n)(n-1)/2=Θ(n^2)

插入排序算不算快?

对于较小的n,还算是很快的,但是对于大n,它就不算快了,更快的一种排序算法是归并排序。

 

8 引入归并排序

8.1 归并排序步骤

S1:如果n=1,那么结束;

S2:否则,递归地排序A[1,…[n/2]]和A[[n/2]+1,…n]

S3: 将2个已经排序好的表合并在一起(’Merge’)

8.2 归并子程序 

这里用到了一个归并子程序,这也是这个算法的关键。

假设2张已经按照从小到大排序好的表,如何合并?

因为最小的元素一定是2张表中首个元素之一。所以每次对比2张表中最小元素即可。

举例:

1 7 11 13 15 19

3 4 9 18 20 22

首先比较1,3,取1,然后第一个表划掉1

然后比较7和3,取3,然后第二个表划掉3

然后比较7和9,取7,然后第一个表划掉7

以此类推

这个过程运行时间是Θ(n),因为每个元素用了一次对比,是常数时间,然后做了n次,所以是Θ(n),也就是线性时间。

 

9 归并排序运行时间分析(递归树方法)

所以T(n)={Θ(1) if n=1;

                   2T(n/2)+Θ(n)(if n>1)(注意:Θ(n)+Θ(1)=Θ(n)}

已经知道递归式子了,那么运行时间如何求解?

可以用递归树的方法求解运行时间,具体的在Lecture2 会讲到,我们只要考虑n>1的情况即可

此时T(n)可以表示成为2*T(n/2)+C*n

构造递归树方法:

1 先把递归式子的左半部分写出来

 .NET 配置文件简单使用第4张

注意:式子右边树上的叶子加起来正好就是T(n),比如第一颗树,T(n)=Cn+T(n/2)*2

高度(层数)约为log(n)(视频上写着log(n),个人觉得应该是表示log2(n),后面也直接用log(n)表示)

这里强调“约”,是因为n不一定是2的整数幂,但是接近。

假设n是2的整数幂,那么讲到Θ(1),正好有log(n)步,n->n/2->n/4->…1,实际上是log(n)的常数倍。

最底层的叶子节点数目是n

T(n)表达式?

为了得到T(n),考虑把所有树上叶子加起来是多少。

除了最后一层,每一层的综合都是C*n,最后一层不一定是C*n,因为边界情况可能存在别的值,记为Θ(n)

所以总数T(n)=Cn*log(n)+Θ(n)(第一项比第二项高)=Θ(n*logn)

考虑渐近情况下,Θ(n*logn)比Θ(n2)要快,所以归并排序在一个充分大的输入规模n下将优于插入排序。

 

10待解决疑问:

1比如递归树原理,有待下一讲一些理论

2 为什么在渐近情况下,Θ(n*logn)<<Θ(n2)

标签: .NETAgileFR

面试跟序列有关的问题汇总

Posted on 2013-10-05 20:13 huhuuu 阅读(422) 评论(5编辑 收藏

  面试中比较多会出序列有关的面试题,所以就总结下

(1)一个长度N为的序列,求前K小的数

  1.排序 N*log(N)

  2.最大堆N*log(K)

  3.有最坏时间复杂度O(n)的算法,因为我们可以在O(n)的时间内找到未排序数组里面第k小的数的值,然后再遍历一下数组,把值小于等于第k小的全都输出(感谢 huangnima)

(2)有两个长度为N的有序序列A和B,在A和B中各任取一个数可以得到N^2个和,求这N^2个和中最小的N个。

  1.比较直观的想法是将A与B的数字相加后排序,时间复杂度O(N*N*log(N*N))

  2.考虑到要求的是求最小的N个数字,所以从这里考虑优化,维护一个大小为N的最小堆 log(N),对于N^N个数字的选择有没有优化方法,有!

     可以把这些和看成n个有序表:

    – A[1]+B[1] <= A[1]+B[2] <= A[1]+B[3] <=…

    – A[2]+B[1] <= A[2]+B[2] <= A[2]+B[3] <=…

    –…

    – A[n]+B[1] <= A[n]+B[2] <= A[n]+B[3] <=…

    当然并不用计算所有的和

    综上所述,可以采用K路归并:

    就是最小堆的元素增加一个状态量(下标),记录当前列最小值所在位置,下次遍历时从这里开始!

    总的时间复杂度O(N*log(N))

.NET 配置文件简单使用第5张 View Code

(3)有两个有序序列长度分别N,M。在A和B中各任取一个数可以得到N*M个和,求这N*M个和某个数字K是第几大的元素。

  1.暴力N*M排序,时间复杂度O(N*M*log(N*M))

  2.贪心的思想: 随着i增大,j只会逐渐减小或不变,时间复杂度O(N+M)

  注意查询的数字有多个相同数字的情况

.NET 配置文件简单使用第6张 View Code

 (4)有两个有序序列长度分别N,M。在A和B中各任取一个数可以得到N*M个和,求这N*M个中第K大的元素是

  Nmin,Mmin,Nmax,Mmax分别表示N,M的最小值域最大值 

  对[Nmin+Mmin,Nmax+Mmax]进行二分,二分出一个结果,判断这个值是第几大(第三个问题),再二分判读直到出结果

  时间复杂度O(log(-Nmin-Mmin+Nmax+Mmax)*(N+M))

  提供练习的传送门:http://ac.jobdu.com/problem.php?pid=1534

(5)查找一个数列中为K的个数有几个

  1.如果有序 两次二分,先找K最左端的位置,在二分k最右端的位置

.NET 配置文件简单使用第7张 View Code

     2.如果无序,则线性遍历

(6) 给定一个数字序列,查询任意给定区间内数字的最小值。

  1.RMQ O(n*logn)

.NET 配置文件简单使用第8张 View Code

  2.线段树 O(n*logn)

如果有相关的题目,会继续更新

 
 
分类: 贪心专辑在线离线查找,二分查找,三分查找面试准备Boyer-Moore Algorithm(BM算法解析)

Boyer-Moore Algorithm(简称BM算法)

               
一、本文内容
网上有很多论文和blog介绍BM算法,但是论文太理论,blog讲解复杂且不清楚,我花了一天多的时间看懂这个算法,并发现wikipedia中BM算法条目中C代码实现的两个bug,修改、调试并测试过后才正常运行。我希望能用简单明了的方式来看这个算法。只有这样,外在的知识经过加工才能内化为自己的东西。
 
 
二、算法特点
字符串匹配算法中有三个单模式算法,naive算法、KMP算法和BM算法。本文介绍BM算法,据说BM比KMP快3~5倍,还有文本处理软件中的查找(CTRL+F)和替换(CTRL+H)命令用的就是BM算法。
 
BM算法有三个特点:1、从右向左扫描;2、坏字符规则(Bad character shift rule,以下简称Bc);3、好后缀规则(Good suffix shift rule,以下简称Gs)。
 
注:以一个sample说明什么是Bc和Gs
主串:     xyzbGsxyz
模式串: bGsdGs
从右至左扫描,模式串的后缀Gs与主串匹配,则称模式串Gs为好后缀;
模式串与主串出现第一次不匹配,称主串的不匹配字符b为坏字符。
 
 
三、算法描述
参数:模式串(以下称为P),长度为m;
         主串(以下称为T),长度为n;
          
算法在对P与T进行匹配之前,先对P进行预处理,计算出Bc算法对应的Bc表和Gs算法对应的Gs表(Bc表表示P、T第一次不匹配时,T对应字符向右移动的shift值;Gs表表示P已匹配的suffix向右移动的shift值)。其中Bc算法和Gs算法同时进行,并取两者之中的大值。
 
1、Bc规则
为了更好掌握算法特点,直接上图。Bc规则有三种情况,分别见图二、图三。
.NET 配置文件简单使用第9张
.NET 配置文件简单使用第10张
图一. Bc规则的定义
图一说明:B(x)表示P中=T中的Bc字符即x,且最靠右的位置
 
.NET 配置文件简单使用第11张
.NET 配置文件简单使用第12张
图二. Bc规则的两种情况
图二说明:case 1: B(x)<i 即正常情况; case 2:B(x)=0表示P中不存在等于Bc的字符即x,则将P整个移至与T中Bc的后一个字符对齐(蓝色横线)。图中有点小bug(x应该是不存在的)。
 
.NET 配置文件简单使用第13张
.NET 配置文件简单使用第14张
图三. Bc规则的异常情况
图三说明:case 3:与Bc相等的字符出现在P中已匹配的suffix中,意味着i-B(x)<0需要往左移,自然Bc规则在这种情况下将失效。
 
 
2、构造Bc表
 
参数说明:delta1数组即Bc表,索引为字符表(比如ascii码表)对应的字符,值为T失配字符的shift值;pat即P,patlen即m.
 
#define ALPHABET_LEN 256
#define NOT_FOUND patlen
void make_delta1(int *delta1, uint8_t *pat, int32_t patlen) {
    int i;
     
    /*初始化整个字符表的shift值为模式串P的长度(即case 2:出现坏字符时,P中无相同的字符)*/
    for (i=0; i < ALPHABET_LEN; i++) {
        delta1[i] = NOT_FOUND;
    }
    /*从左至右更新相同字符离失配位置(即patlen-1)的最近距离(case 1)*/
    for (i=0; i < patlen-1; i++) {
        delta1[pat[i]] = patlen-1 - i;
    }
}
注:case 3直接忽略就OK。
 
 
3、Gs规则
BM算法的难点和精髓也就是Gs表的构建上。Gs规则同样有三种情况。
.NET 配置文件简单使用第15张
图四. Gs规则的case 1
图四说明:case 1:P中失配字符y之前存在已匹配后缀的副本即t'(限制条件:1、副本最靠近失配字符,2、前导字符不相等z≠y),
 
.NET 配置文件简单使用第16张
图五. Gs规则的case 2
图五说明:case 2:case 1条件不满足,若P存在前缀t''=已匹配后缀t的后缀,则找到最长的t'',失配时,直接移至suffix对 对齐。
 
.NET 配置文件简单使用第17张
图六. Gs规则的case 3
图六说明:case 3:case 1、2都不存在,则失配时,可以直接将P移至首部与T已匹配后缀的尾部对齐(即shift值为P自身长度)
 
 
.NET 配置文件简单使用第18张
图七. Gs的三种case总览
 
 
4、构造Gs表
/*Gs规则case 2:suffix-prefix对,从已匹配后缀[pos, wordlen)判断word是否存在前缀,
   即word[0, suffixlen) == word[pos, wordlen)
*/
int is_prefix(uint8_t *word, int wordlen, int pos) {
    int i;
    int suffixlen = wordlen - pos;
    // could also use the strncmp() library function here
    for (i = 0; i < suffixlen; i++) {
        if (word[i] != word[pos+i]) {
            return 0;
        }
    }
    return 1;
}
画了个图,大概就是这个意思  .NET 配置文件简单使用第19张
 
 
/*Gs规则case 1:suffix-suffix对,从pos向←查找与从P末尾(即已匹配后缀)向←查找相等的最长后缀,
   并返回最长后缀的长度
 */
int suffix_length(uint8_t *word, int wordlen, int pos) {
    int i;
    // increment suffix length i to the first mismatch or beginning of the word
    //比较范围[1, pos]与[patlen-pos, patlen-1], 注意:串word[0..pos]的后缀不包含自身
    for (i = 0; (word[pos-i] == word[wordlen-1-i]) && (i < pos); i++);
    return i;
}
 
参数说明:delta2即Gs表,
/*假设P中p对应的字符失配,p之后全部匹配*/
void make_delta2(int *delta2, uint8_t *pat, int32_t patlen) {
    int p;
    int last_prefix_index = patlen-1;
 
    /*first loop:Gs规则case 2*/
    for (p=patlen-1; p>=0; p--) {
        if (is_prefix(pat, patlen, p+1)) { //从p+1开始的后缀是否存在前缀(p失配)
            last_prefix_index = p+1;      //last_prefix_index记录从右至左最后一个匹配字符的index(即p的右边)
        }
    //若存在前缀,保存最后一个匹配字符的index;否则,保存上次已匹配字符的index
        delta2[p] = last_prefix_index; 
    }
 
    /*second loop:Gs规则case 1,因为case 2是前缀,而中间的子串(可以看做[0,p]的suffix)也可能=P的suffix,
       且有可能不止一个中间子串,故p从左向后进行处理,保存最靠近P的suffix的对应子串前一个字符的shift长度
     */
    for (p=0; p < patlen-1; p++) {
        int slen = suffix_length(pat, patlen, p);         //末尾向左对应的从p向左的最长后缀的长度
        /*若已匹配suffix-suffix对的前导字符不匹配,保存向左的第一个失配字符的shift长度(即suffix-suffix对的起始位置之差)
      若匹配,则为前缀即case 2,无需改变shift值
         */
        if (slen > 0 && pat[p - slen] != pat[patlen-1 - slen]) {   //slen=0, 即case 3:delta2[patlen-1-slen]=delta2[patlen-1]=patlen
            delta2[patlen-1 - slen] = patlen-1 - p ; 
        }
    }
}
 
四、算法实现
 
下面是完整的代码,源代码来源于wikipedia的BM算法条目,但是发现两个bug,分别位于make_delta2中的@bug1和@bug2,修改后运行正确。
 
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
 
#define ALPHABET_LEN 256
uint32_t patlen;
#define NOT_FOUND patlen
#define max(a, b) ((a < b) ? b : a)
 
/*构造Bc表*/
void make_delta1(int *delta1, uint8_t *pat, int32_t patlen) {
    int i;
     
    /*初始化整个字符表的shift值为模式串P的长度(即case 2:出现坏字符时,P中无相同的字符)*/
    for (i=0; i < ALPHABET_LEN; i++) {
        delta1[i] = NOT_FOUND;
    }
    /*从左至右更新相同字符离失配位置(即patlen-1)的最近距离(case 1)*/
    for (i=0; i < patlen-1; i++) {
        delta1[pat[i]] = patlen-1 - i;
    }
}
 
/*Gs规则case 2:suffix-prefix对,从已匹配后缀[pos, wordlen)判断word是否存在前缀,
   即word[0, suffixlen) == word[pos, wordlen)
*/
int is_prefix(uint8_t *word, int wordlen, int pos) {
    int i;
    int suffixlen = wordlen - pos;
    // could also use the strncmp() library function here
    for (i = 0; i < suffixlen; i++) {
        if (word[i] != word[pos+i]) {
            return 0;
        }
    }
    return 1;
}
 
/*Gs规则case 1:suffix-suffix对,从pos向←查找与从P末尾(即已匹配后缀)向←查找相等的最长后缀,
   并返回最长后缀的长度
 */
int suffix_length(uint8_t *word, int wordlen, int pos) {
    int i;
    // increment suffix length i to the first mismatch or beginning of the word
    //比较范围[1, pos]与[patlen-pos, patlen-1], 注意:串word[0..pos]的后缀不包含自身
    for (i = 0; (word[pos-i] == word[wordlen-1-i]) && (i < pos); i++);
    return i;
}
 
/*构造Gs表*/
void make_delta2(int *delta2, uint8_t *pat, int32_t patlen) {
    int p;
    int last_prefix_index = patlen-1;
 
    /*first loop:Gs规则case 2*/
    for (p=patlen-1; p>=0; p--) {
        if (is_prefix(pat, patlen, p+1)) { //从p+1开始的后缀是否存在前缀(p失配)
            last_prefix_index = p+1;      //last_prefix_index记录从右至左最后一个匹配字符的index(即p的右边)
        }
        //若存在前缀,保存最后一个匹配字符的index;否则,保存上次已匹配字符的index
        delta2[p] = last_prefix_index;                                       //@bug 1: + (patlen-1 - p);
    }
 
    /*second loop:Gs规则case 1,因为case 2是前缀,而中间的子串(可以看做[0,p]的suffix)也可能=P的suffix,
       且有可能不止一个中间子串,故p从左向后进行处理,保存最靠近P的suffix的对应子串前一个字符的shift长度
     */
    for (p=0; p < patlen-1; p++) {
        int slen = suffix_length(pat, patlen, p);         //末尾向左对应的从p向左的最长后缀的长度
        /*若已匹配suffix-suffix对的前导字符不匹配,保存向左的第一个失配字符的shift长度(即suffix-suffix对的起始位置之差)
           若匹配,则为前缀即case 2,无需改变shift值
         */
        if (slen > 0 && pat[p - slen] != pat[patlen-1 - slen]) {   //slen=0, 即case 3:delta2[patlen-1-slen]=delta2[patlen-1]=patlen
            delta2[patlen-1 - slen] = patlen-1 - p ;                    //@bug 2: + slen;
        }
    }
}
 
/*打印预处理得到的Bc表和Gs表*/
void print_pre_table(int *delta1, int *delta2, uint8_t *pat, uint32_t patlen){
      uint32_t i;
      printf("模式串:%s ", pat);
      printf("坏字符shift表: ");
      for (i=0; i < patlen-1; i++) {
          printf("(%c, %d) ", pat[i], delta1[pat[i]]);
      }
      printf("(其他字符, %d) ", NOT_FOUND);
 
      printf(" 好后缀shift表: ");
      for (i=0; i < patlen; i++) {
           printf("(%u, %d) ", i, delta2[i]);
      }
}
 
/*BM算法主框架*/
uint8_t boyer_moore (uint8_t *string, uint32_t stringlen, uint8_t *pat, uint32_t patlen) {
    uint32_t i;
    int delta1[ALPHABET_LEN];
    int *delta2 = (int *)malloc(patlen * sizeof(int));
    make_delta1(delta1, pat, patlen);
    make_delta2(delta2, pat, patlen);
    print_pre_table(delta1, delta2, pat, patlen);
 
    i = patlen-1;
    while (i < stringlen) {
        int j = patlen-1;
        while (j >= 0 && (string[i] == pat[j])) {
            --i;
            --j;
        }
        if (j < 0) {
            free(delta2);
        return i+1;       //返回T中匹配的位置
        }
 
        i += max(delta1[string[i]], delta2[j]);   //j失配( [j+1, patlen)已匹配 ),i向右移动的距离取主串T中坏字符delta1[string[i]]与模式串P中好后缀delta2[j]的大者
    }
    free(delta2);
  return -1;
}
 
int main()
{
  uint8_t pat[]="abracadabra";
  uint8_t txt[]="abracadabtabradabracadabcadaxbrabbracadabraxxxxxxabracadabracadabra";
  patlen = sizeof(pat)/sizeof(pat[0]) - 1;
  uint32_t n = sizeof(txt)/sizeof(txt[0]) - 1;
 
  uint8_t ans=boyer_moore(txt, n, pat, patlen);
  printf(" 匹配位置:%d ", ans);
  return 0;
}
 
 
五、算法测试
T:abracadabtabradabracadabcadaxbrabbracadabraxxxxxxabracadabracadabra
P:abracadabra
 
运行结果
.NET 配置文件简单使用第20张
 
 
 
参考:
BM算法的applet演示(貌似省略了Gs规则)http://people.cs.pitt.edu/~kirk/cs1501/animations/String.html
另外形式的两种实现:
 
 
标签: 算法

免责声明:文章转载自《.NET 配置文件简单使用》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇读配置文件操作对象WPF 自定义TextBox,可控制键盘输入内容下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

poj2349最小生成树prim算法

题目:有s个satellite channels,但有p(p>s)个地方,若任意两个地方有satellite channels,则无视该距离,并且剩余的地方只能与其他地方通过无线电连接,需要距离,且需要的距离只与最大距离有关,问该最大距离的最小值(大概是这样啦)分析:实际上就是求最小生成树中的第p-s大的数,可以先通过prim算法生成最小生成树,然后...

最短路径算法(I)-Floyed、dijkstra

弗洛伊德算法(Floyed-Warshall) 适用范围及时间复杂度 该算法的时间复杂度为O(N^3),适用于出现负边权的情况。 可以求取最短路径或判断路径是否连通。可用于求最小环,比较两点之间的大小。 (什么??你不知道什么是负边权??戳->http://t.cn/Ef7pbu6) 核心思想 对于任意一个K点,i到j的距离有两种可能:要么经过k点,要...

国家集训队论文分类整理(转)

距离ACM/ICPC的时间越来越少了,选择性地看一些集训队论文是很有必要的。 (在此给已经看过所有论文的神牛跪了= =)http://www.cnblogs.com/AbandonZHANG/archive/2012/07/21/2601889.html 所以,我在此整理了一下,供大家参考。 组合数学 计数与统计 2001 - 符文杰:《P...

几种实现延时任务的方式

一、应用场景 在需求开发过程中,我们经常会遇到一些类似下面的场景:1)外卖订单超过15分钟未支付,自动取消2)使用抢票软件订到车票后,1小时内未支付,自动取消3)待处理申请超时1天,通知审核人员经理,超时2天通知审核人员总监4)客户预定自如房子后,24小时内未支付,房源自动释放 那么针对这类场景的需求应该如果实现呢,我们最先想到的一般是启个定时任务,来扫描...

举个栗子看如何做MySQL 内核深度优化

本文由云+社区发表 作者介绍:简怀兵,腾讯云数据库高级工程师,负责腾讯云CDB内核及基础设施建设;先后供职于Thomson Reuters和YY等公司,PTimeDB作者,曾获一项发明专利;从事MySQL内核开发工作8年,具有丰富的优化经验;在分布式存储等领域有较丰富经验。 MYSQL数据库适用场景广泛,相较于Oracle、DB2性价比更高,Web网站、日...

字符串匹配算法

一、简介 文本信息可以说是迄今为止最主要的一种信息交换手段,而作为文本处理中的一个重要领域——字符串匹配,就是我们今天要说的话题。(原文还特意提及文本数据数量每18个月翻一番,以此论证算法必须要是高效的。不过我注意到摩尔定律也是18个月翻番,这正说明数据的增长是紧紧跟随处理速度的,因此越是使用高效的算法,将来待处理的数据就会越多。这也提示屏幕前的各位,代码...