精华内容
下载资源
问答
  • 启发上文介绍了二分图的最大匹配,自然而然地想到,这个算法能否推广到一般图? 在一般图中,找到一个matching,使得它包含的边数最多。 我们发现上文算法中的基本概念augmenting path并非依赖于二分图的性质,这是...

    启发

    上文介绍了二分图的最大匹配,自然而然地想到,这个算法能否推广到一般图?

    在一般图中,找到一个matching,使得它包含的边数最多。

    我们发现上文算法中的基本概念augmenting path并非依赖于二分图的性质,这是一个好消息。
    那么二分图特殊在哪儿呢?

    G为二分图的充要条件是|G|>2且图中没有奇长度的环。

    那么这个性质有什么影响呢?能否在原算法的基础上通过修改解决这个问题呢?如果不能,又要退回到更基本的概念和想法上。
    这便是算法延展的一般思路。

    症结

    我们“照葫芦画瓢”,继续使用ST标号的方法,只不过这次没有boy和girl的说法了。得到如下标号规则:
    1. 如果xS, y is free(x,y)E,(x,y)matching,那么给 y标上T。
    2. 如果 yT, x is free,(x,y)E,(x,y)matching,那么给 x标上S。
    好像没什么不妥,顺着这个思路继续模仿:

    1. 清空标记,将所有单身的点标成S,并从他们开始搜索。
    2. 搜索x时,遍历相邻的点y,根据上面的规则给y标号,继续搜索y。
    3. 若搜索到单身的点,则证明从起始点到搜到的单身点有一条augmenting path,增广并进入下一个阶段。
    4. 若3不能满足,则证明已经得到最大匹配,退出。

    等等,哪里不对。怎么会这么简单?
    当然不可能这么简单。原因在2中按照上述规则给y标号时,存在标号冲突。
    情况1:先被标成T,后被标成S。
    这种情况只会在一对配偶都被标成T,之后其中一个点开始扩展的时候发生冲突。考虑到标成T的点在扩展的时候,根据规则2,只能去标它的配偶点且标成S,我们可以在实现的时候,每标到一个T,立刻标记它的配偶点为S,从而消除这种冲突
    情况2:先被标成S,后被标成T。
    首先如果两边来自不同单身点的搜索,则找到一条增广路,但来自同一个单身点怎么办?想一下匈牙利算法的实现,我们每次只扩展一个单身点,这种方法下,一旦出现冲突,一定是来自同一个单身点的扩展。
    这种情况不能避免,处理方法是这个算法的核心所在——缩点
    我们来看论文中的一幅图。
    情况二的例子
    在这幅图中,i在搜到相邻的j点时,j已经标为S,但此时根据规则,若j空闲应该标T。
    我们仔细观察后发现,找到搜索树的最近公共祖先后,整个结构形成一个奇环,成为带花树的花苞(blossom)。保证奇环内匹配数最多的情况下,只能有一个点向外匹配,且无论它最初被标成S或T。这个性质像极了一个S点!
    所以我们将图中圆圈内的这个奇环缩点,继续操作就可以了。注意每个stage要重新拆开哦!

    实现

    这个算法说起来很容易,但是实现起来比较困难,特别是缩点之后在找augmenting path时还有拆开,而且可能多重缩点。
    但暴力地写法还是很容易的,搜索顺序用DFS比较好,不用求搜索树的LCA,老老实实创建新点,把环中的边都加进去云云。时间复杂度O(n4)
    考虑上述瓶颈,主要在每次缩点都要枚举一遍边,以及判断两个点是否在同一个blossom中。前者可以用链表存边解决,后者可以用并查集解决。但是层层剥离blossom求augmenting path的实现难度依然很大。我建议另建一棵树维护blossom的内外结构。时间复杂度O(n(m+n))
    另一种论文上提到的方法是使用BFS的顺序,blossom不真正缩点,而是用一种巧妙的双向链表把边连起来,再把blossom中所有点加入搜索队列。时间复杂度O(n3)。//貌似网上大多采用这种实现,而且还是本学期图论助教最先实现的。

    展开全文
  • 一般在文本处理的时候,会遇到字符处理的问题,Python提供的re模块是一个做文本匹配的基础模块,它主要是通过制定的规则找出文本中符合条件的子字符或者子字符串。 1、字符匹配 根据条件,做单个的字符匹配,字符...

    目录

    1、字符匹配

    1.1、匹配字符的规则

    1.2、匹配字符的个数

    2、re的简单使用

    3、模块级函数


    一般在文本处理的时候,会遇到字符处理的问题,Python提供的re模块是一个做文本匹配的基础模块,它主要是通过制定的规则找出文本中符合条件的子字符或者子字符串。

    1、字符匹配

        根据条件,做单个的字符匹配,字符匹配的规则由两部分组成,一个是匹配的字符形式,比如大小写字母、数字等字符,另一部分则是字符重复的次数。

        在正则匹配中的元字符有: . ^ $ * + ? { [ ] \ | ( )。这些元字符的主要作用是跟其它的标准字符一起组成匹配的规则,所以可以认为它们是带有其它作用的特殊字符。

    1.1、匹配字符的规则

    (1)通过正反括号"["和"]"确定要匹配的字符

             “[]”这个括号里面可以例举出想要匹配的字符集合,也可以通过限定字符集的范围确定。例如,[abc] 和[a-c]都表示匹配"a", "b", 或 "c"中的任意一个字符。

            如果字符本身是元字符,那么它在 “[]”里面的时候就是一个普通字符,例如:[[]就是表示匹配字符“[”。

    (2)取反元字符 ^ 的使用

            在方括号中,首字符如果使用取反字符^的话,就表示匹配初括号中的字符之外的其它字符。例如,[^5] 将匹配除 "5" 之外的任意字符。

    (3)反斜杠“\”的使用

           反斜杠主要的作用的是,将那些在正则匹配中有特殊意义的字符转换成普通的字符,或者将普通字符标记成它代表的特殊意义。例如,在Python中,我们用一对英文双引号""来标记字符串,这个时候如果我们在字符串中想把英文双引号当做一个普通字符来处理,那么就可以用反斜杠"\"来转义一下,"\""表示的就是普通英文双引号"这个字符,如果再加一个斜杠"\\"",则又转义回了其作为字符串标记的功能,那么就需要再加一个同样功能的双引号跟它配对,比如"\\"""就表示两个字符串的标记。反斜杠也可以用来确定要匹配的字符类型,规则如下:

    \d  匹配任何十进制数;它相当于类 [0-9]。
    \D  匹配任何非数字字符;它相当于类 [^0-9]。
    \s  匹配任何空白字符;它相当于类  [ \t\n\r\f\v]。
    \S  匹配任何非空白字符;它相当于类 [^ \t\n\r\f\v]。
    \w  匹配任何字母数字字符;它相当于类 [a-zA-Z0-9_]。
    \W  匹配任何非字母数字字符;它相当于类 [^a-zA-Z0-9_]。

    这样特殊字符都可以包含在一个字符类中。如,[\s,.]字符类将匹配任何空白字符或","或"."。

    (4)元字符“.”的使用

           . 它匹配除了换行字符外的任何字符,在 alternate 模式(re.DOTALL)下它甚至可以匹配换行。"." 通常被用于你想匹配“任何字符”的地方。 

    1.2、匹配字符的个数

            re中主要使用*、+、?还有花括号{}来确定匹配的字符的个数

    (1)*指定前一个字符可以被匹配零次或更多次,而不是只有一次。ca*t 将匹配 "ct" (0 个 "a" 字符), "cat" (1 个 "a"), "caaat" (3 个 "a" 字符)等等

    (2)+表示匹配一或更多次。用同一个例子,ca+t 就可以匹配 "cat" (1 个 "a"), "caaat" (3 个 "a"), 但不能匹配 "ct"。

    (3)? 匹配一次或零次;你可以认为它用于标识某事物是可选的。例如:home-?brew 匹配 "homebrew" 或 "home-brew"。

    (4){m,n},其中 m 和 n 是十进制整数。该限定符的意思是至少有 m 个重复,至多到 n 个重复。举个例子,a/{1,3}b 将匹配 "a/b","a//b" 和 "a///b"。它不能匹配 "ab" 因为没有斜杠,也不能匹配 "ab" ,因为有四个。

     

    2、re的简单使用

            re的常用方法如下:

    方法/属性 作用
    match() 决定 RE 是否在字符串刚开始的位置匹配
    search() 扫描字符串,找到这个 RE 匹配的位置
    findall() 找到 RE 匹配的所有子串,并把它们作为一个列表返回
    finditer() 找到 RE 匹配的所有子串,并把它们作为一个迭代器返回

            如果没有匹配到的话,match() 和 search() 将返回 None。如果成功的话,就会返回一个 `MatchObject` 实例,其中有这次匹配的信息:它是从哪里开始和结束,它所匹配的子串等等。从使用的例子来看,match和search一般返回第一个匹配到的子串,而findall和finditer返回的是符合条件的所有子串。

    (1)match的使用

             step 1:通过re.compile()生成一个pattern对象

             step 2:通过pattern去匹配字符串

             例1:

    import re
    
    s = "ahahha123"
    p = re.compile("[a-z]+")
    print(p.match(s))
    
    output:
    字符串s的匹配结果为: <_sre.SRE_Match object; span=(0, 6), match='ahahha'>

           结果的查看, MatchObject 实例也有几个方法和属性;最重要的那些如下所示:

    方法/属性 作用
    group() 返回被 RE 匹配的字符串
    start() 返回匹配开始的位置
    end() 返回匹配结束的位置
    span() 返回一个元组包含匹配 (开始,结束) 的位置

     

       如下面的例子可以看到,匹配的对象主要返回的是匹配的字符串,以及匹配到的字符串的开始和结束的位置。例2:

    s = "ahahha123"
    p = re.compile("[a-z]+")
    result = p.match(s)
    print(result.group())
    print(result.start())
    print(result.end())
    print(result.span())
    
    output:
    ahahha
    0
    6
    (0, 6)

     (2)search()的使用

    s = "123ahahha1xxx23wuwuuwu"
    p = re.compile("[a-z]+")
    result = p.search(s)
    print(result.group())
    print(result.start())
    print(result.end())
    print(result.span())
    
    output:
    ahahha
    3
    9
    (3, 9)

    (3)findall()

            返回所有符合条件的子串,感觉一般文本处理里面最实用的用法。

    s = "123ahahha1xxx23wuwuuwu"
    p = re.compile("[a-z]+")
    result = p.findall(s)
    print(result)
    
    
    output:
    ['ahahha', 'xxx', 'wuwuuwu']

    (4)finditer()的用法

            这个方法返回的结果跟match和search一样,是一个匹配到的对象,然后再通过对象的方法返回具体匹配到的值和位置。

    s = "123ahahha1xxx23wuwuuwu"
    p = re.compile("[a-z]+")
    result = p.finditer(s)
    for i in result:
        print(i)
        print(i.group())
        print(i.start())
        print(i.end())
        print(i.span())
    
    
    output:
    <_sre.SRE_Match object; span=(3, 9), match='ahahha'>
    ahahha
    3
    9
    (3, 9)
    
    <_sre.SRE_Match object; span=(10, 13), match='xxx'>
    xxx
    10
    13
    (10, 13)
    
    <_sre.SRE_Match object; span=(15, 22), match='wuwuuwu'>
    wuwuuwu
    15
    22
    (15, 22)

    3、模块级函数

         之前都是先调用compile()先生成一个pattern,然后再用不同的搜索方式去字符串中匹配这个pattern,这个方式比较麻烦,还有一种比较简单的用法,也是比较符合直观思维的用法,如下:

    print(re.match(r'From\s+', 'From age amk'))
    print(re.search(r'From\s+', 'From age amk'))
    print(re.findall('}', '{block}}'))
    
    
    output:
    <_sre.SRE_Match object; span=(0, 5), match='From '>
    <_sre.SRE_Match object; span=(0, 5), match='From '>
    ['}', '}']

    参考原文:Python正则表达式操作指南

    展开全文
  • 带花树算法 UOJ#79. 一般图最大匹配

    千次阅读 2017-03-09 20:58:42
    然而对于每一个有偶数点的环,环里面无论怎么匹配都是可以的,然而假如有奇数的环就不一样了。 不想写废话了。。 ————————————正文—————————— 时间复杂度大概是O(n^2)吧 首先,

    带花树算法

    能解决的问题:一般图最大匹配http://uoj.ac/problem/79

    匈牙利算法可以解决二分图匹配的问题,但是因为二分图有一个特殊的性质,那就是不会出现一个有奇数点的环。然而对于每一个有偶数点的环,在环里面无论怎么匹配都是可以的,然而假如有奇数的环就不一样了。

    不想写废话了。。

    ————————————正文——————————

    时间复杂度大概是O(n^2)

    首先,思路肯定是找增广路,也是让每一个没匹配的点去尝试进行匹配,看是否可以有新的匹配。

    首先,在没出现奇环之前,我们把他当做一个普通的二分图来看,是没毛病的。

    于是有了id这一个数组,id有三种不同的状态

    -1:没访问过  0:S点  1:T

    首先我们假设一开始要增广的点是一个S

    由于写的是广搜,所以我们需要一个数组,pre,表示,假设在这次匹配中,第i个点的配偶根别人跑了,他该和谁配对。

     

    然后对于找到方案的这一段代码就很简单了。。

     

     

    那么接下来的问题是如何来进行寻找增广路的操作了

    假设现在我们在队列中待处理的点为x

    首先,假如我们遍历到一个以前没有访问过的点y,那么我们就让他的配偶继续进行增广。

    Q:为什么是让他的配偶去呢?

    A:因为我们现在是要假设x要与y相连啊,拿肯定是看看y的配偶是否可以换人啦,这一步感觉和普通的匈牙利是没什么太大的不同的

     

     

    然后我们可以发现一个问题啊,那就是我们每一个待扩展的点x都是一个S点,嗯,都是S点。这就说明每一个点都是由S点扩展出来的。

     

    那么除了上面的情况外,剩下的就是环了

     

    假设我们出现的是一个偶环,那么就直接无视——至于为什么,大概想一下就可以了

     

    假如我们发现的是一个奇环,那就有大大的不一样了,因为假如我们这个环中有任意一个点可以在外面找到增广路的话,这个环就变成了一个偶数环,问题就解决了。

     

    比如说上面这个图,X就是我们要增广的点,假如出现了这样一个环,那么只要1,2,3,4随便一个点可以找到增广路,那么X就可以有配偶啦^_^

    然后下文我们假设这个环的突然出现时因为出现了2————3这一条边

     

    铺垫:我们在处理完一个奇环后,我们是可以把他试做一个点的(因为这个环中只要有一个点成功就行),所以这里写了一个并查集,来使点变成一个环,我的并查集下文用的是f,当然,这个大点是一个S点。

     

    那么我们怎样知道他是不是一个奇环呢?

    也很简单,只要我们访问到的点也是S点就是了。这个自己想一下应该也可以想出来

     

    那么剩下的操作就是如何让他们去遍历了。

    首先我们要知道这个环长什么样吧,比如说在上图中,2,3肯定是由之前的同一个点增广时所牵扯出来的,也就是有一个点使得他们间接连通现在才回出现环,上图就是x,所以我们要先知道他是从哪里来的。于是我们需要一个lca,当然这个lca也写得比较巧妙~~

     

     

    然后就是处理环了,分两段进行,

     

    一段就是处理2x这一条路,另一段就是处理3x这一条路,两个的操作是一样的,所以只需要写一个就可以了。

     

    其中一个操作肯定是让图中所有的点去增广,因为一开始的S点肯定都已经入队增广了,所以我们要新加入的点就只有之前的T点了。

     

    其次就是维护pre了,这个过程也比较简单,就是先让出现新边的两条pre互连,然后让上面的每一个T点与扩展他的S点相连。然后假如我们在其中任意有一个点找到增广路了,根据pre,你就可以得出一条没有冲突的匹配关系。这个我觉得自己脑补一下就好了。

     

    还是举个栗子吧。。

    还是上面的图,假如你现在T4找到了增广路,那么3就会去找2,2的原配偶1就会去找x

    假如是S3找到了增广路,那么4就会找到x1,2关系不用变,这个和没环没啥区别。

    完结撒花~~



    全代码大部分都是照着别人的写的。。


     

    #include<cstdio>
    #include<cstring>
    #define swap(x,y) {int tt=x;x=y;y=tt;}
    const int N=505*2;
    const int M=124750*2;
    int f[N];
    struct qq
    {
    	int x,y;
    	int last;
    }s[M];
    int num,last[N];
    int n,m;
    void init (int x,int y)
    {
    	num++;
    	s[num].x=x;s[num].y=y;
    	s[num].last=last[x];
    	last[x]=num;
    }
    int match[N];
    int id[N];//这个点是什么点
    //-1:没访问过  0:S点  1:T点 
    int q[N];//要扩展的队列————也就是我们要尝试帮谁换配偶 
    int pre[N];//在这次过程中,x的新配偶是谁
    int Tim,vis[N];//对于lca的标记以及时间轴 
    int find (int x)
    {
    	if (f[x]==x) return f[x];
    	f[x]=find(f[x]);
    	return f[x];
    }
    int lca (int x,int y)//寻找lca 
    {
    	Tim++;
    	while (vis[x]!=Tim)
    	{
    		if (x!=0)
    		{
    			x=find(x);//先找到花根 
    			if (vis[x]==Tim) return x;
    			vis[x]=Tim;
    			if (match[x]!=0) x=find(pre[match[x]]);
    			//因为在之前我们知道,每一个S点的配偶(也就是T点)的pre 都是指向他的父亲的,于是就直接这么跳就可以了
    			//还有要注意的是,一定要先去到花根,因为他们现在已经是一个点了,只有花根的pre才指向他们真正的父亲 
    			else x=0;
    		}
    		swap(x,y);
    	}
    	return x;
    }
    int st,ed;
    void change (int x,int y,int k)//环  出现的是x---y的连边  已知根是k 
    {
    	while (find(x)!=k)
    	{
    		pre[x]=y;
    		int z=match[x];
    		id[z]=0;q[ed++]=z;if (ed>=N-1) ed=1;
    		if (find(z)==z) f[z]=k;
    		if (find(x)==x) f[x]=k;
    		y=z;x=pre[y];
    	}
    }
    void check (int X)//尽量帮助x寻找增广路 
    {
    	for (int u=1;u<=n;u++) {f[u]=u;id[u]=-1;}
    	st=1;ed=2;
    	q[st]=X;id[X]=0;
    	while (st!=ed)
    	{
    		int x=q[st];
    		for (int u=last[x];u!=-1;u=s[u].last)
    		{
    			int y=s[u].y;
    			if (match[y]==0&&y!=X)
    			//当然match[X]=0,但X(这次来寻找配偶的点)并不是一个可行的东西,所以不能算可行解 
    			{
    				pre[y]=x;//先假设他与x相连
    				int last,t,now=y;
    				while (now!=0)//当然,这次来的X的match是为0,要是能更新到0就是结束 
    				{
    					t=pre[now];//now新的配偶
    					last=match[t];//理所当然啦 
    					match[t]=now;match[now]=t;
    					now=last;
    				}
    				return ;
    			}
    			if (id[y]==-1)//找到一个没有访问过的点————进行扩展
    			{
    				id[y]=1;
    				pre[y]=x;//先假设他与x相连
    				id[match[y]]=0;q[ed++]=match[y];
    				if (ed>=N-1) ed=1;
    			}
    			else if (id[y]==0&&find(x)!=find(y))//出现一个以前未处理过的奇环
    			{
    				int g=lca(x,y);
    				change(x,y,g);change(y,x,g);
    			}
    		}
    		st++;
    		if (st>=N-1) st=1;
    	}
    }
    int main()
    {
    	memset(vis,0,sizeof(vis));Tim=0;
    	memset(match,0,sizeof(match));
    	num=0;memset(last,-1,sizeof(last));
    	scanf("%d%d",&n,&m);
    	for (int u=1;u<=m;u++)
    	{
    		int x,y;
    		scanf("%d%d",&x,&y);
    		init(x,y);init(y,x);
    	}
    	for (int u=1;u<=n;u++) 
    		if (match[u]==0) 
    			check(u);
    	int ans=0;
    	for (int u=1;u<=n;u++)
    		if (match[u]!=0) ans++;
    	printf("%d\n",ans/2);
    	for (int u=1;u<=n;u++) printf("%d ",match[u]);
    	return 0;
    }

    肯定写错了很多东西。。。。。。


    展开全文
  • 下面几篇数据结构理论方面的文章,将会从字符串匹配算法开始说,因为刷leetcode被很多字符串的题难到了,就来学习极客时间的文章了。 我们使用的字符串查找函数,比如java中的indexOf(),python中的find()函数等,...

    下面几篇数据结构理论方面的文章,将会从字符串匹配算法开始说,因为刷leetcode被很多字符串的题难到了,就来学习极客时间的文章了。

    我们使用的字符串查找函数,比如java中的indexOf(),python中的find()函数等,他们底层就是依赖接下来要讲的字符串匹配算法。

    字符串匹配算法有很多,会分成四节来说。今天会讲两种比较简单的,好理解的,分别是:BF算法和RK算法。还有后面会介绍到的比较难,但是会更加高效的,他们是:BM算法和KMP算法。

    但是上面提到的算法都是单模式串匹配的算法,也就是在一个串中同时查找多个串,他们分别是Trie树和AC自动机。

    今天说的算法中:RK算法是BF算法的改进,它巧妙借助了我们前面讲过的哈希算法,让匹配的效率有了很大的提升。

    那RK算法是如何借助哈希算法来实现高效字符串匹配的呢

    BF算法

    BF算法中的Brute Froce的缩写,中文叫做暴力匹配算法,也叫朴素匹配算法。

    从名字可以看出,这种算法的字符串匹配方法很暴力,当然也很简单好理解,就是性能不高。

    在开始讲解这个算法之前,我先对两个概念下定义。他们分别是 “主串”“模式串” 。我们把主串的长度记做n,模式串的长度记做m。因为我们是在主串中查找模式串,所以n>m。

    作为最简单,最暴力的字符串匹配算法,BF算法的思想可以用一句话来概括,那就是,我们在主串中,检查起始位置分别是0、1、2···n-m且长度为m的n-m+1个子串,看有没有跟模式串匹配的。

    举一个例子来看:

    在这里插入图片描述

    从上面的算法思想和例子,我们可以看出,在极端情况下,比如主串是“aaaaaaaaa····aaaaaaa”这种的,模式串为“aaaaaaab”。我们每次都比对m个字符,要比对n-m+1次,所以,这种算法的最坏时间复杂度是O(n*m)。

    尽管理论上,BF算法的时间复杂度很高,是O(n*m),但是在实际开发中,缺是一个比较常用的字符串的匹配算法,为什么呢?

    第一,在实际的软件开发中,大部分情况下,模式串和主串的长度都不会太长。而且每次模式串与主串中的子串匹配的时候,当中途遇到不能匹配的字符的时候,就可以停止了,不需要把m个字符都比对一下。所以,尽管理论上的最坏的情况时间复杂度是O(n*m),但是统计意义上,算法执行效率要比这个好很多。

    第二,朴素字符串的匹配算法思想很简单,代码实现也非常简单。简单意味着不容易出错,如果有bug也容易暴露和修复。在工程中,在满足性能要求的前提下,简单是首选。这也是我们常说的KISS原则。

    所以,在实际的开发中,暴力法就足够了。

    RK算法

    RK算法全称叫做Rabin-Karp算法,我个人觉得,它其实就是刚刚讲的BF算法的升级版。

    我们知道关于BF算法,如果模式串长度为m,主串长为n,那在主串中,就会有n-m+1个长度为m的子串,我们只需要暴力地对比这n-m+1个子串和模式串,就可以找出主串与模式串匹配的子串。

    但是,每次检查主串与子串是否匹配,需要依次比对每个字符,所以BF算法的时间复杂度就比较高,是O(n*m),和BF算法是一样的。但如果引入哈希,提升占用内存,降低时间复杂度,也是可以的,然后我来介绍一下:

    RK算法的思路如下:我们通过哈希算法对n-m+1个子串分别求哈希值,然后逐个与模式串的哈希值比大小。如果某个子串的哈希值与模式串相等,那就说明对应的子串匹配到了。

    在这里插入图片描述

    不过,通过哈希算法计算子串的哈希值的时候,我们需要遍历子串的每个字符。尽管模式串与子串比较的效率提高了,但是算法整体的效率也并没有提高。有没有方法可以提高哈希算法计算子串哈希值的效率呢?

    这就需要哈希算法设计得十分有技巧了:假设我们要匹配的字符串的字符集只包含k个字符,我们可以用一个k进制数来表示一个子串,这个k进制数转化成十进制数,作为子串的哈希值。表述起来有点抽象,我举一个例子:

    比如要处理的字符串只包含a~z这26个小写字母,那我们就用二十六进制来表示一个字符串。我们把a~z这 26个字符映射到0~25这26个数字,a就表示0,b就表示1,以此类推,z表示25。

    在十进制的表示法中,一个数字的值是通过下面的方式计算出来的。对应到二十六进制,一个包含a到z这 26个字符的字符串,计算哈希的时候,我们只需要把进位从10改成26就可以。

    在这里插入图片描述
    现在,为了方便解释,在下面的讲解中,我假设字符串中只包含a~z这26 个小写字符,我们用二十六进制来表示一个字符串,对应的哈希值就是二十六进制数转化成十进制的结果。

    这种哈希算法有一个特点,在主串中,相邻两个子串的哈希值的计算公式有一定关系。我这有个个例子,你 先找一下规律,再来看我后面的讲解。

    在这里插入图片描述

    从这里的例子来看,我们很容易就能看出这样的规律:相邻两个子串s[i-1]和s[i],对应的哈希值计算有交集的存在,也就是说,我们可以使用s[i-1]的哈希值很快的计算出s[i]的哈希。如果用公式就是这样子:

    在这里插入图片描述

    不过有一个小细节需要注意:那就是26^(m-1)这部分的计算,我们通过查表的方法去提高效率。我们事先计算好26 ^ 0、26 ^1、26 ^2……26 ^(m-1),并且存储在一个长度为m的数组内,公式中的次方对应数组的下标,当我们需要计算26的x次方的时候,就可以从数组的下标为x的位置取值就行了。

    在这里插入图片描述

    然后我们来分析一下,现在的时间复杂度:

    整个RK算法分为:

    1. 扫描主串,获取子串的哈希,这部分就是简单的遍历计算,所以是On
    2. 模式串哈希值和每个子串的哈希值比较是O1,总共比较n-m+1个子串,所以时间复杂度还是On,所以复杂度就是On

    这里还有一个问题就是,模式串很长,响应的主串中的子串也会很长,通过上面的哈希算法计算得到的哈希值就可能会很大,如果超过了计算机中整形数据能表示的范围,那该如何解决呢?

    哈希的算法设计有很多种方式,举刚才情况的例子来说。假设字符串中只包含了a~z这26个字母,那么我们给每一个字符replace成数字,a是1,b是2…z是26,这样哈希值就小很多了。但哈希碰撞的概率就上来了,不过我们也可以手动混淆,降低冲突的概率。

    但是新的问题又来了

    我们假设,总模式串的哈希值为63,这个63=62+1=60+3…那么就会让不同的模式串有相同的哈希,这又该如何解决呢?

    实际上,解决方法很简单,只需要再去对比子串和模式串本身就好了。

    所以,哈希算法的冲突概率要相对控制得低一些,如果存在大量冲突,就会导致RK算法的时间复杂度退 化,效率下降。极端情况下,如果存在大量的冲突,每次都要再对比子串和模式串本身,那时间复杂度就会 退化成O(n*m)。但也不要太悲观,一般情况下,冲突不会很多,RK算法的效率还是比BF算法高的。

    小结

    BF算法是最简单、粗暴的字符串匹配算法,它的实现思路是,拿模式串与主串中是所有子串匹配,看是否 有能匹配的子串。所以,时间复杂度也比较高,是O(n*m),n、m表示主串和模式串的长度。不过,在实际 的软件开发中,因为这种算法实现简单,对于处理小规模的字符串匹配很好用。

    RK算法是借助哈希算法对BF算法进行改造,即对每个子串分别求哈希值,然后拿子串的哈希值与模式串的 哈希值比较,减少了比较的时间。所以,理想情况下,RK算法的时间复杂度是O(n),跟BF算法相比,效率 提高了很多。不过这样的效率取决于哈希算法的设计方法,如果存在冲突的情况下,时间复杂度可能会退 化。极端情况下,哈希算法大量冲突,时间复杂度就退化为O(n*m)。

    展开全文
  • Q:一般匹配和二分图匹配问题的差别是在于哪里呢? A:一般图可能存在奇环。 解释,奇环:即从A点到B点存在一条偶数长度的路径,也存在一条奇数长度的路径,两条路径结合构成一个和为奇数的路径也是环 我们可以通过...
  • Opencv——立体匹配

    2019-11-26 17:16:31
    立体匹配,就是匹配两个不同的摄像头视图的3D点,只有两个摄像头的重叠视图内的可视区域才能被计算,这就是为什么要使摄像头靠近前向平行了。 立体匹配的目的是通过匹配得到视差。 立体匹配的方法 BM算法: 该算法...
  • 采用CLMANDROID上实现的人脸匹配

    千次阅读 2016-05-05 22:51:12
    图片和视频中的人脸匹配(人脸识别)是一项非常有用的技术,尤其是随着近来自带摄像功能的手机普及。通过对人脸的五官识别,我们可以实现自己的眼球追踪,照相中的表情(包括微笑,眨眼等)识别,或者照相/...
  • 这篇文章讲无权二分图(unweighted bipartite graph)的最大匹配(maximum matching)和完美匹配(perfect matching),以及用于求解匹配的匈牙利算法(Hungarian Algorithm);不讲带权二分图的最佳匹配。 1. 二分...
  • 串的模式匹配

    千次阅读 2019-10-14 17:44:06
    字串(模式串)的定位操作 主串(也称做目标串)S中...当模式匹配成功时,函数返回模式串T的第一个字符主串S中的位置;当模式匹配失败时,函数返回-1 朴素的模式匹配算法(Brute-Force算法) BF算法的主要思想是...
  • KMP模式匹配算法

    2015-08-01 19:12:19
    我们假设一个字符串匹配的很一般的背景:已知S、T两个字符串,其中S为原字符串,T为需要我们匹配的字符串,主要任务就是完成到S中匹配T字符串。如果能够匹配到的话返回对应的字符串的位置。   我们假设原字符串...
  • 模式匹配之路

    2019-10-03 17:23:16
    之前我的文章中,介绍了KMP,BM,AC,WM等几个经典的模式匹配算法,本文我将对常用的模式匹配算法做一小结。 模式匹配算法的关系图如下所示: 这里有几个我的文章中暂时没有涵盖的算法,这里做一简要说明。 ...
  • halcon 形状匹配

    万次阅读 2018-10-31 14:01:57
    指定模板图像区域(也可以draw_rectangle1画一个矩形) Row1 := 188 Column1 := 182 Row2 := 298 Column2 := 412 gen_rectangle1 (ROI, Row1, Column1, Row2, Column2) reduce_domain (ModelImage, ROI, ImageROI...
  •   本文我们来给大家详细介绍下Nginx中的核心配置文件中的Location匹配规则。   location会尝试根据用户请求中的URI来匹配上面的/uri表达式,如果可以匹配,就选择location{}块中的配置来处理用户请求。当然,...
  • 1./chosen节点中bootargs属性就是内核启动的命令行参数,它里面可以指定根文件系统在哪里,第一个运行的应用程序是哪一个,指定内核的打印信息从哪个设备里打印出来。 2./memory中的reg属性指定了不同板子内存的...
  • 匈牙利算法: 匈牙利算法几乎是二分图匹配的... 一般匹配等其他点的个数较小的匹配问题 举个以前写的博客的例子 通过数代人的努力,你终于赶上了剩男剩女的大潮,假设你是一位光荣的新世纪媒人,你的手...
  • 这里,我们需要通配符进行匹配查找。而执行匹配查找的关键字就是LIKE。 SQL语句支持很多种通配符,其中可以和LIKE一起搭配使用的就是通配符%和_了。 百分号通配符%可以匹配任意长度的字符,甚至包括零字符。 ...
  • 字符串匹配算法

    2015-09-15 15:59:48
    第一篇随笔,开始写博客生涯。... 字符串匹配主要是关于模式串与主串匹配的问题。关于这个问题,有很多方法。网上也有不少例子,借鉴了不少,以下就介绍下面几种算法。    (1)BF算法(常规算法)  
  • KMP字符串模式匹配详解

    千次阅读 2013-03-24 16:25:12
    KMP字符串模式匹配通俗点说就是一种一个字符串中定位另一个串的高效算法。简单匹配算法的时间复杂度为O(m*n);KMP匹配算法。可以证明它的时间复杂度为O(m+n).。 一. 简单匹配算法 先来看一个简单匹配算法的函数...
  • (记)路由匹配优化方案测试 偶然听到xxx同事接到一个网关性能优化任务...于是分析路由匹配的耗时主要在哪里。以下为目前路由匹配的伪代码: // 这里了spring的matcher来进行路径匹配 private AntPathMatcher m
  • Nginx的详细匹配规则

    2019-09-21 11:31:36
    本文来自网上转载,如有侵权,请联系告知,博主马上删除! nginx 语法规则: ...^~开头表示uri以某个常规字符串开头,理解为匹配 url路径即可。nginx不对url做编码,因此请求为/static/20%/aa,可以被规则...
  • 就是能让绝大部分入射到边界上的光都透射的那个完美匹配层PML,哪设置的? PML的说明见下面链接:...
  • 《算法导论》一书中有一句话,我认为说的非常透彻:“这两个程序有很多相似之处,因为它们都是一个字符串对模式P的匹配:KMP-MATCHER是文本T针对模式P的匹配,COMPUTE-PREFIX是模式P针对自己的匹配。”
  • 如果Python写爬虫的话,正则匹配在re包里面,然后一般也需要用到urllib2(网络请求响应),BeautifulSoup(响应内容解析),当然也有Scrap框架,也支持Python3.6了,这个方法对静态页面爬取没有问题; 如果是爬取需要...
  • IntentFilter 的匹配规则 action 的匹配规则 category 的匹配规则 data 的匹配规则 总结过滤规则 注意 ThanksIntentIntent 是一个消息传递对象,我们可以使用它启动其他应用组件完成特定的任务。我们可以通过
  • Elasticsearch 权威教程 - 模糊匹配

    千次阅读 2018-03-01 21:56:50
    将字面切分成很多字、词(word)建立索引,match查询query中的term来匹配索引中的字、词。match查询提供了文档数据中是否包含我们需要的query中的单、词,但仅仅这样是不够的,它无法提供文本中的字词之间的关系。 ...
  • OpenCV中的匹配算法

    千次阅读 2013-07-01 22:23:12
    我使用的是OpenCV中的cvStereoRectify,得出校准参数之后cvRemap来校准输入的左右图像。这部分的代码参考的是Learning OpenCV 十二章的例子。   校准之后,就可以立体匹配了。立体匹配OpenCV里面有两种方法,一...
  • 单模式匹配算法-KMP算法

    千次阅读 2012-07-22 01:09:02
    模式匹配一般分为单模式匹配和多模式匹配。当然,通常都是指字符序列中的匹配问题。   单模式匹配,对一个较长的字符序列,调用一次算法只匹配一个模式串。  本文展示的是单模式匹配中的经典算法--KMP算法...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 80,716
精华内容 32,286
关键字:

匹配一般用在哪里