1.正则表达式

正则表达式:是字符串的规则,只是检测字符串是否符合条件的规则而已
1.检测某一段字符串是否符合规则
2.将符合规则的匹配出来
re模块:是用来操作正则表达式的

2.正则表达式组成

字符组:[] 一个字符组描述的是一个位置上的字符规则,但是不能从大到小来规定范围,字符组内所有的范围都是ascii来排序的,字符组更灵活一点
  [0-9] 匹配一个数字范围
  [a-z] 匹配一个小写字母
  [A-Z] 匹配一个大写字母
  [A-Za-z0-9] 匹配英文字母和大小写 左边必须是ascii最小的   注意:遇到在字符组当中的'-'是有特殊意义的,如果想取消'-'的特殊意义,要转义一下
  例如:[1-2] 是匹配不是1就是2的数据,但是我想匹配1,-,2那?需要转义一下[1\-2] 元字符:匹配的一个字符的内容
  \d: 使用\d表示匹配任意从0-9的数字,使用字符组能匹配到一个固定的范围,但是\d表示的时从0-9太固定
\w:匹配所有的数字字母下划线,标识符
-t:匹配tab制表符
\n:匹配换行符
\s:匹配所有的空格换行制表符
\b:表示一个单词的边界,要匹配以XX结尾的单词
反义词:
\W:除了数字字母下划线的所有
\D:除了数字之外的所有
\S:除了空白之外的所有
.:匹配换行符之外的所有字符 非字符组:[^123] 除了123的都匹配 ‘^’必须和[]组合才叫非字符组
 开始符'^' 结束符'$' 
  一个字符串只有一个开始和一个结束,^永远在一个规则的最前面,$永远在一个字符串的最末尾
  ^hello$ -->匹配一个hello,规定了这个字符串的长度,在字符串检测的时候通常加上^和$符号
  [^123] :匹配的是不以123开头的
 | 或的意思,竖杠的左边或右边是一个规则,永远把相对长的规则放在左边
 分组:()--> www\.(baidu|oldboy)\.com

 量词:表示的是匹配的次数,在量词的范围内尽可能的多的匹配,叫贪婪匹配 
  {n} 表示出现n次
   {n,} 表示出至少出现n次
   {n,m}表示存出现n-m次
   ?:匹配额0次或1次
   +:匹配1次或多次
   *:匹配0次或多次
  ?和+的范围等于*的范围    匹配电话号码:^1[3-9]\d{9}$:
匹配整数:^[1-9]\d*|0&
匹配小数:\d+\.\d+
匹配整数或者小数:\d+(\.\d+)? -->里面的()是分组的意思,?表示要不()里面的一起出现,不要就都不出现 \d+\.?\d*
匹配15位身份证号码:^[1-9]\d{14}$
匹配18位身份证号码:^[1-9]\d{16}[\dx]$
 #转义字符:在字符串外部加上一个r来不转义
贪婪匹配:尽可能的多匹配 a.*x 回溯算法
惰性匹配:尽可能的少匹配 .*? ,两个量词同时出现时表示非贪婪 a.*?x 从a开始匹配,遇到一个x就停止
      就是量词后面加上?就是惰性匹配,匹配到第一个就停止
 

re模块

import re
s='kobe123jordan46' re.findall(pattern,string,flags=0)
  ret = re.findall('\d+',s)
  print()
  查看所有,返回一个列表
  注意:在正则条件不一样的情况下返回的内容不一样 '\d+'是贪婪的匹配所有数字组成一个字符串
re.search(pattern,string,flags=0)
  ret =re.search('\d+',s)
  print(ret)
  print(ret.group())
  返回一个结果对象或结果集
  通过group()取值只取到第一个,如果没有符合条件的group就会报错,一般用if判断一下是否为空 re.match(pattern,string,flags=0)
  
ret = re.match('\s+',s)
  print(ret)
  返回一个结果对象或结果集
  使用match的话,相当于在正则表达式前面加上^,在程序里面匹配的是'^\s',是找以字符串开头的,没有的话就返回None
  使用search完全可以代替match,只要在search的表达式前面加上^就可以
  re.search('^\s+','kobe123') == re.match('\s+','kobe123') re.sub(pattern,repl,string,count=0)
  ret = re.sub('\d+','sb',s,count=1)
  print(ret)
  count不写的话默认替换所有,返回一个字符串
  
re.subn(pattern,repl,string,count=0)
  ret = re.sun('\d+','sb',s)
  print(ret)
  默认返回一个元组,并且返回被替换了多少次
re.split(pattern,string,maxsplit=1)
  ret = re.split('\d+',s,maxsplit=1)
  print(ret)
  默认返回一个列表,不加maxsplit就全部分割 re.compile(pattern)
  par = re.compile('\d+')
  ret = par.findall(s)
  print(ret)
  对于一个经常重复使用的正则表达式,我们可以先进行一次编译,之后只要在使用这个表达式就直接拿出来使用
  就好了,这样做节省时间和及其资源,减少解析时间个转成机器码的时间
  re.compile()是一个优化函数,提升执行效率减少不必要的时间和性能的开销 re.finditer(pattern,string)
  par = re.compile('\d+')
  ret = par.finditer('kob123kobe123kobe123kobe123'*200)
  for item in ret:
    print(item.group())
  返回的是一个生成器结果集,通过group方法取出每一个数据
  看到iter应该能想到是生成器原理,通过循环每次使生成器返回一个数据,这样和使用生成器读取大文件一样,节省内存
  能够提前编译一个正则表达式,当同一个正则表达式被多次使用时,可以直接使用节省时间 分组

  s= """
    <h1>哇哈哈哈<h1>
    <h2>哈哈哈哈<h2>
    <title>科比</title>
   """

  findall()和分组:分组在findall当中会默认的优先被显示出来
    ret = re.findall('>\w+<',r'<title>科比<\title>')
    print(ret[0].strip('<>'))
    分组在findall当中会默认的优先被显示出来,如果不想优先显示出来就在分组中添加(?:正则规则)表示取消这个正则的优先显示
 
    ret = re.findall('>\w+<',r'<title>科比<\title>')
    print(ret[0].strip('<>'))

    ret = re.findall('>(\w+)<',r'<title>科比<\title>')
    print(ret) # findall永远优先显示分组中的内容

    ret = re.findall('www\.(?:baidu|nba)\.com',r'www.baidu.com')
    print(ret)

    ret = re.findall('\d+(?:\.\d+)?',r'1.23+2.34')
    print(ret)

  split()和分组:会保留被切掉的在分组中内容
    ret = re.split('\d(\d)','alex84wusir73')
    print(ret)

  search()和分组:不受到分组的影响

    ret = re.search(r'<(\w+)>(\w+)<\\(\w+)>',r'<title>科比<\title>')
    print(ret.group(0)) # 不受到分组的影响
    print(ret.group(1))
    print(ret.group(2))

分组命名

  #分组命名就是把一个要匹配的东西取一个名字,在以后调用返回的时候回方便,直接使用名字就OK了
  #如果有取值的有两个相同的名字的话可以复制为(?P=a),必须要写的分组里面,不然的不能叫分组命名了

    ret = re.search(r'<(?P<tab1>\w+)>(?P<content>\w+)<\\(\w+)>',r'<title>科比<\title>')
    print(ret.group(0)) # 不受到分组的影响
    print(ret.group('tab1')) # 不受到分组的影响
    print(ret.group('content')) # 不受到分组的影响

     特殊的需求:把这个字符串全部匹配出来<h1>wahaha</h2></h1>
    par = '<\w+>.*?</\w+>'
    ret = re.search('<(?P<tag>\w+)>.*</(?P=tag)>','<h1>wahaha</h2></h1></h3>')
    print(ret.group())

    par = '<\w+>.*?</\w+>'
    ret = re.search(r'<(\w+)>.*</\1(是使用第几个分组名字)>','<h1>wahaha</h2></h1></h3>')
    print(ret.group())

    当我们要匹配的内容混在不想匹配的内容中
    只能把不想要的也匹配出来,然后去掉不想要的就是想要的

    下列我只想取整数部分
    ret = re.findall('\d+\.\d+|(\d+)','1-2*(60+(-40.35/5)-(-4*3))') #取出来不包含40.35,但是包含"",因为findall遇到分组是优先匹配,所以匹配到小数就放弃,但是必须得返回一个元素,就返回""
    ret = re.findall('\d+\.\d+|\d+','1-2*(60+(-40.35/5)-(-4*3))')  #取出来的包含40.35
    ret.remove('')
    print(ret)

遇见分组
    findall 优先显示分组中的内容
    split 保留被切掉的分组内的内容
    search 可以通过组的索引取值
    取消分组的特殊行为(?:正则)

常见的使用正则解决问题的方式#
  1.把整个结构描述下来,对想要的进行分组#
  2.把不想要的也匹配出来,然后用手段删掉

1. 数字:^[0-9]*$
2. n位的数字:^\d{n}$
3. 至少n位的数字:^\d{n,}$
4. m-n位的数字:^\d{m,n}$
5. 零和非零开头的数字:^(0|[1-9][0-9]*)$
6. 非零开头的最多带两位小数的数字:^([1-9][0-9]*)+(.[0-9]{1,2})?$
7. 带1-2位小数的正数或负数:^(\-)?\d+(\.\d{1,2})?$
8. 正数、负数、和小数:^(\-|\+)?\d+(\.\d+)?$
9. 有两位小数的正实数:^[0-9]+(.[0-9]{2})?$
10. 有1~3位小数的正实数:^[0-9]+(.[0-9]{1,3})?$
11. 非零的正整数:^[1-9]\d*$ 或 ^([1-9][0-9]*){1,3}$ 或 ^\+?[1-9][0-9]*$
12. 非零的负整数:^\-[1-9][]0-9"*$ 或 ^-[1-9]\d*$
13. 非负整数:^\d+$ 或 ^[1-9]\d*|0$
14. 非正整数:^-[1-9]\d*|0$ 或 ^((-\d+)|(0+))$
15. 非负浮点数:^\d+(\.\d+)?$ 或 ^[1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0$
16. 非正浮点数:^((-\d+(\.\d+)?)|(0+(\.0+)?))$ 或 ^(-([1-9]\d*\.\d*|0\.\d*[1-9]\d*))|0?\.0+|0$
17. 正浮点数:^[1-9]\d*\.\d*|0\.\d*[1-9]\d*$ 或 ^(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*))$
18. 负浮点数:^-([1-9]\d*\.\d*|0\.\d*[1-9]\d*)$ 或 ^(-(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*)))$
19. 浮点数:^(-?\d+)(\.\d+)?$ 或 ^-?([1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0)$
2.校验字符的表达式
1. 汉字:^[\u4e00-\u9fa5]{0,}$
2. 英文和数字:^[A-Za-z0-9]+$ 或 ^[A-Za-z0-9]{4,40}$
3. 长度为3-20的所有字符:^.{3,20}$
4. 由26个英文字母组成的字符串:^[A-Za-z]+$
5. 由26个大写英文字母组成的字符串:^[A-Z]+$
6. 由26个小写英文字母组成的字符串:^[a-z]+$
7. 由数字和26个英文字母组成的字符串:^[A-Za-z0-9]+$
8. 由数字、26个英文字母或者下划线组成的字符串:^\w+$ 或 ^\w{3,20}$
9. 中文、英文、数字包括下划线:^[\u4E00-\u9FA5A-Za-z0-9_]+$
10. 中文、英文、数字但不包括下划线等符号:^[\u4E00-\u9FA5A-Za-z0-9]+$ 或 ^[\u4E00-\u9FA5A-Za-z0-9]{2,20}$
11. 可以输入含有^%&',;=?$\"等字符:[^%&',;=?$\x22]+
12. 禁止输入含有~的字符:[^~\x22]+
3.特殊需求表达式
1. Email地址:^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$
2. 域名:[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(/.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+/.?
3. InternetURL:[a-zA-z]+://[^\s]* 或 ^http://([\w-]+\.)+[\w-]+(/[\w-./?%&=]*)?$
4. 手机号码(可根据目前国内收集号扩展前两位开头号码):^(13[0-9]|14[5|7]|15[0|1|2|3|5|6|7|8|9]|18[0|1|2|3|5|6|7|8|9])\d{8}$
5. 电话号码("XXX-XXXXXXX"、"XXXX-XXXXXXXX"、"XXX-XXXXXXX"、"XXX-XXXXXXXX"、"XXXXXXX"和"XXXXXXXX):^(\(\d{3,4}-)|\d{3.4}-)?\d{7,8}$
6. 国内电话号码(0511-4405222、021-87888822):\d{3}-\d{8}|\d{4}-\d{7}
7.15位身份证号:^[1-9]\d{5}\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{2}$
8.18位身份证号:^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$
9. 帐号是否合法(字母开头,允许5-16字节,允许字母数字下划线):^[a-zA-Z][a-zA-Z0-9_]{4,15}$
10. 密码(以字母开头,长度在6~18之间,只能包含字母、数字和下划线):^[a-zA-Z]\w{5,17}$
11. 强密码(必须包含大小写字母和数字的组合,不能使用特殊字符,长度在8-10之间):^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,10}$
12. 日期格式:^\d{4}-\d{1,2}-\d{1,2}
13. 一年的12个月(01~09和1~12):^(0?[1-9]|1[0-2])$
14. 一个月的31天(01~09和1~31):^((0?[1-9])|((1|2)[0-9])|30|31)$
15. 钱的输入格式:
(1)有四种钱的表示形式我们可以接受:"10000.00" 和 "10,000.00", 和没有 "分" 的 "" 和 "10,000":^[1-9][0-9]*$
(2)这表示任意一个不以0开头的数字,但是,这也意味着一个字符""不通过,所以我们采用下面的形式:^(0|[1-9][0-9]*)$
(3)一个0或者一个不以0开头的数字.我们还可以允许开头有一个负号:^(0|-?[1-9][0-9]*)$
(4)这表示一个0或者一个可能为负的开头不为0的数字.让用户以0开头好了.把负号的也去掉,因为钱总不能是负的吧.下面我们要加的是说明可能的小数部分:^[0-9]+(.[0-9]+)?$
(5)必须说明的是,小数点后面至少应该有1位数,所以"10."是不通过的,但是 "" 和 "10.2" 是通过的:^[0-9]+(.[0-9]{2})?$
(6)这样我们规定小数点后面必须有两位,如果你认为太苛刻了,可以这样:^[0-9]+(.[0-9]{1,2})?$
(7)这样就允许用户只写一位小数.下面我们该考虑数字中的逗号了,我们可以这样:^[0-9]{1,3}(,[0-9]{3})*(.[0-9]{1,2})?$
(8)1到3个数字,后面跟着任意个 逗号+3个数字,逗号成为可选,而不是必须:^([0-9]+|[0-9]{1,3}(,[0-9]{3})*)(.[0-9]{1,2})?$
备注:这就是最终结果了,别忘了"+"可以用"*"替代如果你觉得空字符串也可以接受的话(奇怪,为什么?)最后,别忘了在用函数时去掉去掉那个反斜杠,一般的错误都在这里
16. xml文件:^([a-zA-Z]+-?)+[a-zA-Z0-9]+\\.[x|X][m|M][l|L]$
17. 中文字符的正则表达式:[\u4e00-\u9fa5]
18. 双字节字符:[^\x00-\xff] (包括汉字在内,可以用来计算字符串的长度(一个双字节字符长度计2,ASCII字符计1))
19. 空白行的正则表达式:\n\s*\r (可以用来删除空白行)
20. HTML标记的正则表达式:<(\S*?)[^>]*>.*?</\1>|<.*? /> (网上流传的版本太糟糕,上面这个也仅仅能部分,对于复杂的嵌套标记依旧无能为力)
21. 首尾空白字符的正则表达式:^\s*|\s*$或(^\s*)|(\s*$) (可以用来删除行首行尾的空白字符(包括空格、制表符、换页符等等),非常有用的表达式)
22. 腾讯QQ号:[1-9][0-9]{4,} (腾讯QQ号从10000开始)
23. 中国邮政编码:[1-9]\d{5}(?!\d) (中国邮政编码为6位数字)
24. IP地址:\d+\.\d+\.\d+\.\d+ (提取IP地址时有用)
25. IP地址:((?:(?:25[0-5]|2[0-4]\\d|[01]?\\d?\\d)\\.){3}(?:25[0-5]|2[0-4]\\d|[01]?\\d?\\d))

常见的正则表达式

 other re.S和re.M

Python 的re模块内置函数几乎都有一个flags参数,以位运算的方式将多个标志位相加。其中有两个模式:单行(re.DOTALL, 或者re.S)和多行(re.MULTILINE, 或者re.M)模式。
它们初看上去不好理解,但是有时又会非常有用。这两个模式在PHP和JavaScripts里都有。 单行模式 re.DOTALL
在单行模式里,文本被强制当作单行来匹配,什么样的文本不会被当作单行?就是里面包含有换行符的文(或者是网页源码),比如:
This is the first line.\nThis is the second line.\nThis is the third line.
点号(.)能匹配所有字符,换行符例外。现在我们希望能匹配出整个字符串,当用点号(.)匹配上面这个字符串时,在换行符的地方,匹配停止。 1.在打印的时候直接打印出三行
print (a)
This is the first line.
This is the second line.
This is the third line.
2.在不使用re.S的时候会按行匹配
  import re
  ret = re.findall('^This.*line',a)
  print(ret)
  在上面的例子里,即使是默认贪婪(greedy)的匹配,仍然在第一行的结尾初停止了匹配.
3.使用re.S的时候
  import re
  ret = re.findall('^This.*line',a,re.S)
  print(ret)
  而在单行模式下,换行符被当作普通字符,被点号(.)匹配. 多行模式 re.MULTILINE
在多行模式里,文本被强制当作多行来匹配。正如上面单行模式里说的,默认情况下,一个包含换行符的字符串总是被当作多行处理。但是行首符^和行尾符$仅仅匹配整个字符串的起始和结尾。
这个时候,包含换行符的字符串又好像被当作一个单行处理。
在下面的例子里,我们希望能将三句话分别匹配出来。用re.findall( )显示所有的匹配项
a = 'This is the first line.\nThis is the second line.\nThis is the third line.' 1.在打印的时候直接打印出三行
print (a)
This is the first line.
This is the second line.
This is the third line.
2.在不使用re.S的时候会按行匹配
import re
ret = re.findall('^This.*line$',a)
print(ret)
  默认点号不匹配换行符,所以匹配的为空
3.使用re.S的时候
  import re
  ret = re.findall('^This.*line',a,re.M)
  ret = re.findall('^This.*?line',a,re.M)
  print(ret)
  匹配出了整句话,因为默认是贪婪模式
4.用问号切换成非贪婪模式:
仍然是整句话,这是因为^和$只匹配整个字符串的起始和结束。
在多行模式下,^除了匹配整个字符串的起始位置,还匹配换行符后面的位置;$除了匹配整个字符串的结束位置,还匹配换行符前面的位置. 总结:在单行模式下,点号(.)匹配所有的字符,所以可以说,单行模式改变了点号(.)的匹配行为,在多行模式下,多行模式改变了^和$的匹配行为

分组

返回系列

python基础之 正则表达式,re模块的更多相关文章

  1. python基础之正则表达式 re模块

    内容梗概: 1. 正则表达式 2. re模块的使⽤ 3. 一堆练习正则表达式是对字符串串操作的一种逻辑公式. 我们一般使用正则表达式对字符串进行匹配和过滤.使用正则的优缺点: 优点: 灵活,功能性强, ...

  2. 十七. Python基础(17)--正则表达式

    十七. Python基础(17)--正则表达式 1 ● 正则表达式 定义: Regular expressions are sets of symbols that you can use to cr ...

  3. Python基础之 正则表达式指南

    本文介绍了Python对于正则表达式的支持,包括正则表达式基础以及Python正则表达式标准库的完整介绍及使用示例.本文的内容不包括如何编写高效的正则表达式.如何优化正则表达式,这些主题请查看其他教程 ...

  4. Python基础(十)-模块

    模块初识 一般把不同功能代码放到不同py文件,一个py文件就是一个模块 三种模块库: 1.Python标准库 2.第三方库 3.自定义库 导入模块:import 库名 (Python解释器只认识执行文 ...

  5. python基础(10)--模块

    模块(module) 模块,用一坨代码实现了某个功能的代码集合 类似于函数式编程和面向过程编程,函数式编程则完成一个功能,其他代码用来调用即可,提供了代码的重用性和代码间的耦合.而对于一个复杂的功能来 ...

  6. Python基础(12)--模块

    本文地址:http://www.cnblogs.com/archimedes/p/python-modules.html,转载请注明源地址. 模块简介 如果你退出 Python 解释器重新进入,以前创 ...

  7. python基础之正则表达式

    正则表达式语法 正则表达式 (或 RE) 指定一组字符串匹配它;在此模块中的功能让您检查一下,如果一个特定的字符串匹配给定的正则表达式 (或给定的正则表达式匹配特定的字符串,可归结为同一件事). 正则 ...

  8. python基础之正则表达式。

    简介 就其本质而言,正则表达式是内嵌在python内,由re模块实现,小型的专业化语言,最后由c写的匹配引擎执行.正则表达式(regular expression)描述了一种字符串匹配的模式,可以用来 ...

  9. 第六章:Python基础の反射与常用模块解密

    本课主题 反射 Mapping 介绍和操作实战 模块介绍和操作实战 random 模块 time 和 datetime 模块 logging 模块 sys 模块 os 模块 hashlib 模块 re ...

  10. python记录_day23 正则表达式 re模块

    一. 正则表达式 使用python的re模块之前应该对正则表达式有一定的了解 正则表达式是对字符串操作的一种逻辑公式.我们一般使用正则表达式对字符串进行匹配和过滤. 正则的优缺点: 优点:灵活, 功能 ...

随机推荐

  1. 【python】多进程共享变量Manager

    Manager的复杂结构赋值问题 Manager的字典类型: 如果value是简单类型,比如int,可以直接赋值给共享变量,并可以后续直接修改 如果value是复杂类型 ,比如list,dict,则必 ...

  2. Mac OS X系统下,svn: Can't remove file Operation not permitted.解决方案

    当你的svn出现类似以下错误时,提示Operation not permitted之类的问题,说明项目下 .svn文件夹内的文件权限有问题. 一般是由于windows和mac操作系统同时操作同个svn ...

  3. fire workflow总结

    一.Fire WorkFlow核心1.IPersistenceService存储服务.Fire Workflow 缺省情况下使用hibernate 进行数据库存取.如果你的系统不是使用hibernat ...

  4. tp框架设置404页面

    无法加载模板跳向404页面 /thinkphp/library/think/Dispatcher.class.php中176行     // 加载模块的扩展配置文件             load_ ...

  5. Java Mybatis实现主从同步

    import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; public class DynamicDat ...

  6. (二)Knockout 文本与外观绑定

    Visible Visible binding会依据绑定的数据来决定相应的DOM元素是否隐藏,hidden或visible. 我们首先在js文件部分定义一个view model,这里我创建的是一个ob ...

  7. 一起学Python——数据类型详解

    和学习其他编程语言一样,首先要了解一门语言的数据类型. Python的数据类型有整型.浮点型.字符串.布尔型.日期时间类型.list列表.set集合.tuple元组.dict词典等. 1.整型 就是数 ...

  8. Mybatis Annotation使用小结

    Mybatis Annotation使用小结 之前一直有看过mybatis的注解使用方式,但没有去看过它的原理.今天看springboot-mybatis-annotation使用的时候,debug了 ...

  9. 【java】-- java并发包总结

    1.同步容器类 1.1.Vector与ArrayList异同 1.Arraylist和Vector都是采用数组方式存储数据,都允许直接序号索引元素,所以查找速度快,但是插入数据等操作涉及到数组元素移动 ...

  10. NOIP2009 t3 最优贸易

    题目传送门:洛谷P1073 dalao们都用的tarjan啊拓扑排序啊之类的玩意儿,我这个蒟蒻不会,只想到了极其暴力的分层图最短路 设三个状态 0表示没有发生任何买卖的情况 1表示买了没有卖的情况 2 ...