一开始想要学习正则表达式是想学习自动机算法,后来看自动机算法是正则表达式的引擎,就决定先学一下正则表达式
以下资源取自很多网上资源,包括但不限于 百度百科 , CSDN,博客园的一些博客,我还包括一些国外文章的翻译,在此就不一一给出连接了,如有侵权,请及时联系我,我会尽量按照被侵权方要求解决问题
(辣鸡需要看其他人的资料才能学习)QAQ。。。
/*---------------------自动机--------------------------*/
DFA和NFA自动机是正则表达式的引擎,又叫正则引擎。
因此,我觉得如果想学自动机的一些算法的话,了解正则表达式是必须的。
现在先了解一下正则表达式,毕竟磨刀不误砍柴工,下面是一些东拼西凑的概念:
正则表达式 又称 规则表达式,(Regular Expression),通常用来检索,替换那些符合某个模式(规则)的文本
//------------概念:
正则表达式是对字符串操作的一种逻辑公式,就是事先定义好的一些特定字符,以及这些特殊字符的组合
组成一个 规则 字符串 ,这个 规则字符串 用来表达对字符串的一种过滤逻辑。
//------------简介:
正则表达式是对字符串(普通字符)和特殊字符,操作的一种逻辑公式,
用实现定义好的一些特定字符以及这些特定字符的组合,组成一个 规则字符串
这个规则字符串 用来表达对字符串的一种过滤逻辑 。
正则表达式是一种文本模式,模式描述 在搜索文本时要匹配的一个或多个字符串
//------------目的:
给定一个正则表达式个另一个字符串,我们可以达到两个目的:
1. 给定字符串是符合正则表达式的过滤逻辑(匹配)。
2. 可以通过正则表达式,从字符串中获取我们想要的特定部分。
//------------符号:
匹配样例:对于字符串"testing",可以匹配到"testing"和"testing123",但是匹配不到"Testing"。
\ :转义字符
^ :匹配输入字行首。
$ :匹配输入行尾。
* :匹配前面的子表达式任意次。*等价于{0,}。
+ :匹配前面的子表达式一次或多次(大于等于1次)。+等价于{1,}。
? :匹配前面的子表达式零次或一次。?等价于{0,1}。
{n} :n是一个非负整数。匹配确定的n次。
例:"o{2}"不能匹配"Bob"中的"o",但是能匹配"food"中的两个"o"。
{n,}:n是一个非负整数。至少匹配n次。
例:"o{2,}"不能匹配"Bob"中的"o",但能匹配"foooood"中的所有"o"。
{n,m}:m和n均为非负整数,其中n<=m。最少匹配n次且最多匹配m次。
例:"o{1,3}"将匹配"fooooood"中的前三个"o"为一组,后三个"o"为一组。
注意!!:在逗号和两个数之间不能有空格。
? :当该字符紧跟在任何一个其他限制符('*','+','?',"{n}","{n,}","{n,m}")后面时,匹配模式是非贪婪的。
对于字符串"oooo":
1.非贪婪模式尽可能少地匹配所搜索的字符串。例: "o+"将尽可能多地匹配"o",得到结果["oooo"]
2.而默认的贪婪模式则尽可能多地匹配所搜索的字符串。例: "o+?"将尽可能少地匹配"o",得到结果 ['o', 'o', 'o', 'o']
. :匹配除"\n"和"\r"之外的任何单个字符。
a|b :匹配x或y。
例如,"z|food"能匹配"z"或"food"(此处请谨慎)。"[zf]ood"则匹配"zood"或"food"。
[abc]:字符集合。匹配所包含的任意一个字符。
例:"[abc]"可以匹配"plain"中的"a"。
[^abc]:负值字符集合。匹配未包含的任意字符。
例:"[^abc]"可以匹配"plain"中的"plin"任一字符。
[a-z]:字符范围。匹配指定范围内的任意字符。
例:"[a-z]"可以匹配"a"到"z"范围内的任意小写字母字符。
[^a-z]:负值字符范围。匹配任何不在指定范围内的任意字符。
例:"[^a-z]"可以匹配任何不在"a"到"z"范围内的任意字符。
() :将'(' 和 ')' 之间的表达式定义为"组"(group),并且将匹配这个表达式的字符保存到一个临时区域(一个正则表达式中最多可以保存9个),它们可以用 "\1" 到"\9" 的符号来引用。
| 将两个匹配条件进行逻辑 或 。
例:正则表达式(him|her) 匹配"it belongs to him"和"it belongs to her",但是不能匹配"it belongs to them."。注意:这个元字符不是所有的软件都支持的。
//------------引擎:
正则引擎可以分为两大类,DFA,NFA
DFA引擎:
在线执行,不要求回溯(永远不测试相同字符两次)。DFA引擎确保匹配最长可能的字符串;
DFA引擎只包含有限状态,因此不能匹配具有反响引用的模式;
并且因为他不构造显示扩展,所以不能捕获子表达式。
NFA引擎:
运行 贪婪的 回溯算法, 以 指定顺序 测试正则表达式的所有可能的扩展并接受 第一个匹配项
传统的NFA构造正则表达式的特定扩展以获得成功的匹配,所以他可以捕获子表达式匹配和匹配的反向引用
传统的NFA回溯可以访问完全相同的状态多次(如果通过不同的路径到达该状态)
因此,在最坏的情况下,它的执行速度可能非常慢。
传统的NFA接受它找到的第一个匹配,所以它还可能会导致其他(可能更长)匹配未被发现。
POSIX NFA引擎:(现在不想学,就不看了,以后再补上吧)
这里的自动机特指有限状态自动机,简称FA
根据状态转移的性质又分为确定的自动机 DFA 和非确定的自动机 NFA 。
FA的表达能力等价于正规表达式或者正规文法 。
FA可以看做是一个 有向带权图 。
图的 定点集合 成为自动机的 状态集合,
图的 权值集合 为自动机的 字母集合 。
图的 边 代表了自动机中 状态变化的情况。
此外,根据需要,自动机还需指定 初始状态 和终态。
FA最基本的左右就是形式化描述,而且有利于编程实现,以下开始介绍DFA自动机
//-----------------------DFA自动机 -----------
https://blog.csdn.net/u012061345/article/details/52092436?locationNum=11
https://blog.csdn.net/qq_36827957/article/details/74357283
//-----
考虑仅有字符{a,b}组成的字符串,要求字符串中 字母b 必须成对出现,否则字符串非法。
这个规则实现起来非常简单,不需要自动机也完全可以。但是我们现在考虑是有自动机进行判断。
该规则的正规表达式描述是:(a|bb)*。*运算代表重复若干次,包括0次。
做一个图来表示描述该规则的DFA。
令 状态1 为初始状态,显然在 状态1 上,我们还没有违反规则。因此,经过字母a 以后我们还可以回到 状态1 。
经过 字母b 后就不能回到 状态1 了,此时需要一个新状态,令其为 状态2。
状态2表示 待定的 状态,在这个状态时不能肯定字符串是非法的,但也不是合法的。
在 状态2 上,如果经过 字母b ,就回到了合法的状态(状态1);
如果经过 字母a ,则该字符肯定是非法的。建立一个 状态3 ,表示非法状态。
状态3 比较简单,已经到了非法状态,其后的任何字母都不会改变这个状态了。
因此,该DFA表示为:
1 3
^ ^
|a |a,b
| |
V V
1 2 3
---b--> ---a--->
<--b---
程序实现:
状态和字母都被编码成整数,使用一个矩阵表示状态转移,再写一个函数表示自动机的运行,
对于每个字符串,从状态1开始运行,运行完毕进行状态判断即可。
最后能停留在状态1的字符串才是符合规则的,其他的都是非法的。
代码:
#include<cstdio>
int DFA[4][2]={
{0}, //0 0,1
{1,2}, //1 0,1
{3,1}, //2 0,1
{3,3} //3 0,1
};
int START_STATE=1;
int run(char const word[])
{
int state=START_STATE;
for(char const *p=word;*p;++p)
{
state=DFA[state][*p-'a'];
if(state==3)
return state;
}
return state;
}
int main()
{
char a[],b[],c[];
printf("%d\n",run(a));
printf("%d\n",run(b));
printf("%d\n",run(c));
return 0;
}
//--------------HDU-2206-IP的计算
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2206
ac代码, 错误状态记作0
int const T[17][3]={
// d.other
// 0 1 2
/*0*/ 0,0,0,
/*1*/ 2,0,0,
3,5,0,
4,5,0,
0,5,0,
/*5*/ 6,0,0,
7,9,0,
8,9,0,
0,9,0,
/*9*/ 10,0,0,
11,13,0,
12,13,0,
0,13,0,
/*13*/ 14,0,0,
15,0,0,
16,0,0,
0,0,0
};
inline int judge(char ch)
{
if(ch>='0'&&ch<='9')//是个数
return 0;
if(ch=='.')//是点
return 1;
return 2;//啥都不是
}
int run(char const word[])
{
int state=1;
for(char const *p=word;*p;++p)//遍历每个字符
{
state=T[state][judge(*p)];
if(state==0)
return 0;
}
return state;//
}
inline bool isfinal(int state)
{
return state==14||state==15||state==16;
}
int main()
{
char s[110];
while(gets(s))
{
if(isfinal(run(s))==0)//不符合要求
{
cout<<"NO"<<endl;
continue;
}
int a,b,c,d;
sscanf(s,"%d.%d.%d.%d",&a,&b,&c,&d);
if(a>255||b>255||c>255||d>255)
cout<<"NO"<<endl;
else
cout<<"YES"<<endl;
}
}
//----------POJ-3332-Parsing Real Numbers
题目链接:http://poj.org/problem?id=3332
题目大意就是输入一串字符串,然后判断这个串中是否包含有效的实数(任意书写方式,包括但不限于科学计数法等)
是则输出: LEGAL 否则: ILLEGAL
解题思路:
对于一个 字符串,它的状态有以下几种:
一个符合要求的数字可以拆解成下面的式子:
+- d . d Ee +- d _
1 2 3 4 5 6 7 8 9
然后输入对应的DFA转移数组,下面是AC代码:
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<stdlib.h>
//#include<map>
//#include<set>
#include<deque>
#include<queue>
#include<stack>
#include<bitset>
#include<string>
#include<fstream>
#include<iostream>
#include<algorithm>
using namespace std;
#define ll long long
//#define max(a,b) (a)>(b)?(a):(b)
//#define min(a,b) (a)<(b)?(a):(b)
#define clean(a,b) memset(a,b,sizeof(a))// 水印
//std::ios::sync_with_stdio(false);
const int MAXN=1e5+5;
const ll INF=1e18;
const ll mod=1e9+7;
int DFA[10][6]={
0,0,0,0,0,0,
3,0,0,2,0,0,//数字,+-
3,0,0,0,0,0,//+-之后只能是数字
3,4,6,0,9,0,//数字循环 . Ee 直接空格(结尾)
5,0,0,0,0,0,// . 之后的数字
5,0,6,0,9,0,//数字循环 Ee 空格
8,0,0,7,0,0,//Ee之后的 +-
8,0,0,0,0,0,//+-之后的数字
8,0,0,0,9,0,//数字循环 空格
0,0,0,0,9,0 // 空格
};
int get_char(char ch)
{
if(ch<='9'&&ch>='0')
return 0;
if(ch=='.')
return 1;
if(ch=='E'||ch=='e')
return 2;
if(ch=='+'||ch=='-')
return 3;
if(ch==' ')
return 4;
return 5;
}
bool judge(char *s)
{
int index=1;
for(char *p=s;*p;++p)
{
index=DFA[index][get_char(*p)];
if(!index)
return 0;
}
if(index==3||index==5||index==8||index==9)
return 1;
else
return 0;
}
int main()
{
int T;
cin>>T;
getchar();
while(T--)
{
char s[1100];
gets(s);
char *p=s;
while(*p==' ')//找到第一个字符
p++;
if(judge(p))
cout<<"LEGAL"<<endl;
else
cout<<"ILLEGAL"<<endl;
}
}
//------------------ 扩展链接:
正则表达式 的整理: https://blog.csdn.net/github_36498175/article/details/63262348
python中利用正则表达式爬取信息: https://blog.csdn.net/weixin_41580211/article/details/79089038
正则表达式引擎的构建: https://swtch.com/~rsc/regexp/regexp1.html
未完待续。。。
很快就有