精华内容
下载资源
问答
  • Java:正则表达式匹配

    千次阅读 2019-08-30 09:21:50
    一个字符串就是一个正则表达。 如字符串"\D"在Java中表示非数字([^0-9]),而Java代码里字符串中的'\'符号需要转义,所以要表示这个正则表达式需要用"\\D"。...Pattern类表示的是某种匹配模式。一个Pat...

    一个字符串就是一个正则表达。

    如字符串"\D"在Java中表示非数字([^0-9]),而Java代码里字符串中的'\'符号需要转义,所以要表示这个正则表达式需要用"\\D"


    Java中和正则表达式相关的主要有两个类:

    import java.util.regex.Pattern;
    import java.util.regex.Matcher;
    
    • Pattern类表示的是某种匹配模式。一个Pattern对象和一个正则表达式相关联,而不表示具体的匹配。

      Pattern pattern = Pattern.compile("[a-z]\\d{3}.*");
      
    • Matcher类表示的是具体的匹配。一个Matcher对象和一个具体的字符串相关联,表示在指定模式下的这个字符串的匹配。

      Matcher matcher = pattern.matcher("hello123world");
      

      具体的匹配信息都在Matcher对象中,所以多个Matcher对象可以共享一个Pattern对象。

      查看给定的字符串是否完全匹配指定模式:

      System.out.println(matcher.matches()); // true
      
      // 下面的代码完全等价,这是一个简便方法
      System.out.println(Pattern.matches("[a-z]+\\d{3}.*", "hello123world")); // true
      

    看代码示例:

    import java.util.regex.Matcher;
    import java.util.regex.Pattern;
    
    public class Test {
      public static void main(String[] args) {
        String str = "汽车鲁A12345、京A98765、辽B55555超速";
        String pattern = "(\\D{2})(\\d+)"; 
        
        Pattern r = Pattern.compile(pattern);
        Matcher m = r.matcher(str);
    
        // 给出的整个字符串str是否符合正则表达式pattern
        System.out.println(m.matches()); // false
    
        System.out.println("==========匹配查找");
        while (m.find()) { // 从str开头查找符合正则表达式的子串,每次查找会从上一次找到的位置后继续往后查找
          String print = String.format("分组2出现的位置索引: %d, 分组2内容<%s>", m.start(2), m.group(2));
          System.out.println(print);
        }
    
        System.out.println("==========重置匹配器,重置查找位置");
        m.reset(); // 重置匹配器,下一次不指定索引的查找从str头开始。此句可以省略,看下面注释
        
        int index = 10;
        while (m.find(index)) { // 从指定索引开始查找。由于指定了索引,之前的m.reset()对执行结果没影响,可以省略
          String print = String.format("此次匹配,分组数: %d,匹配的子串为<%s>,分组1内容<%s>,分组2内容<%s>", 
            m.groupCount(), m.group(0), m.group(1), m.group(2));
          System.out.println(print);
          
          index = m.end();
        }
    
        System.out.println("==========重置匹配器,重置输入序列");
        m.reset("鲁B99999"); // 重置匹配器的输入序列
        while (m.find()) {
          String print = String.format("分组2出现的位置索引: %d, 分组2内容<%s>", m.start(2), m.group(2));
          System.out.println(print);
        }
      }
    }
    

    程序输出:

    false
    ==========匹配查找
    分组2出现的位置索引: 4, 分组2内容<12345>
    分组2出现的位置索引: 12, 分组2内容<98765>
    分组2出现的位置索引: 20, 分组2内容<55555>
    ==========重置匹配器,重置查找位置
    此次匹配,分组数: 2,匹配的子串为<京A98765>,分组1内容<京A>,分组2内容<98765>
    此次匹配,分组数: 2,匹配的子串为<辽B55555>,分组1内容<辽B>,分组2内容<55555>
    ==========重置匹配器,重置输入序列
    分组2出现的位置索引: 2, 分组2内容<99999>
    
    展开全文
  • java基础知识

    千次阅读 多人点赞 2019-07-31 19:21:46
    字符串(普通型,增强型)、正则表达式、字符串解析、模式匹配, date类、时间输入/输出格式、 List<V>,Set<V>,Map<K,V>,Stack<E>, 线程(Thread\Runable\线程同步\线程守护\线程...

    java基础知识

    说明

    java的基础知识:
    	继承、接口、抽象类、泛型<T>,
    	字符串(普通型,增强型)、正则表达式、字符串解析、模式匹配,
    	date类、时间输入/输出格式、
    	List<V>,Set<V>,Map<K,V>,Stack<E>线程(Thread\Runable\线程同步\线程守护\线程联合),
    	文件\目录(创建\运行\判断),输入\输出流(I\O){四类流}UI(布局/组件/事件/读取多媒体对象)URL对象,java网络编程(套接制TCP-IP/多线程/UDP数据报/广播数据报/远程调用RIM)数据库操作(JDBC/增删改查/用结果集更新数据库/缓冲/预处理/事务处理/批处理/模糊查询/
    范围查询/复合查询/排序查询)。
    
    Web的基础知识:
    	HTML标签(主体标签/样式标签/组件标签/框架/多媒体)CSS样式表(定义的方式//伪类/引用的4种方式)JavaScript(处理html标签对象/调用方法/使用事件/使用内置对象/应用)JSP(声明/表达式/引用java代码/指令标签{在jsp页面的头部}/动作标签{引入|转发|跳转}
    /动作标签/javabean标签/九大内置对象{范围|生命周期}/Cookie对象)JavaBean(特点/范围)Servlet(成员方法/生命周期/web.xml的配置/核心类/过滤器(4种方式)/监听器/获取初始化
    参数)JSP的两种开发模式(模式1/模式2MVC),
    	Dreamweaver CS6的使用,
    	Tomcat目录结构,
    	Eclipse的使用。
    
    Struts2的基础知识:
    	配置struts.xml,
    	使用struts标签库,
    	定义action,和页面进行数据交互,
    	struts拦截器(定义/配置/使用),
    	struts校验。
    	
    Spring的基础知识:
    	配置applicationContext.xml文件,定义bean,
    	IOC(注入的方式)AOP编程,
    	使用注解,
    	事务,代理,模板,缓冲池,引用外部文件。
    	硬编码方式。
    
    Hibernate的基础知识:
    	配置文件,创建与使用,
    	增删改查,
    	表关联(1-1,1-n,n-n)HQL查询语句。
    
    

    java基础知识 内容.

    1. 继承、类extends,只能有一个父类,super调用父类,
    2. 方法重构(名字相同参数不同),
    3. 多态(子类的多样), final不能继承-可以被匿名类引用,
    4. 上转型。
    5. 接口、interface 名,没有方法体,只能implement实现接口,可以多实现,为实现类提供实现方法的模板规范,可以有成员变量,接口回调。
    6. 抽象类、abstract 类,含有抽象方法的类,可以含有非抽象方法,只能继承不能new,和接口的功能类似。
    7. 泛型类、class 名称<泛型列表>(不能是基本类型),声明泛型 。
      例:Chorus <Student,Button> model=new Chorus <Student,Button>();在声明的时候将泛型列表的Chorus <E,F>的类型确定了,为Student,Button,在泛型类中可以用E,F来表示类型。
    8. 内部类、在类内部定义的类,只能被这个类使用。
    9. 匿名类、将一个类的实现作为一个参数。
    10. 异常类、自定义一个类 MyException继承Exception类,当执行
      例:throw(new MyException())时将会在异常处理模块中执行catch(MyException e){}的部分。
    11. 字符串、引用型,String用””定义,用数组定义,基本的方法(比较,长度,转字符数组,取字符),和其他基本类型之间的pre,to,””+互相转化方法;
      StringBuffer类,可以操作字符串的内容(截取,代替,获取);
      StringTokenizer类,可以解析字符串,有两个构造方法StringTokenizer(字符串,分隔符),分隔符的任意排列组合,返回结果集,hasMoreTokenizer是否为false来判断,nextTokens()来获取下一个对象
    12. 正则表达式、字符串对象调用matches(正则表达式),判断是否和正则表达式匹配,返回boolean类型;字符串对象调用replaceAll(正则表达式,替换的字符串)产生一个将匹配正则表达式的字串替换后的新的字符串对象;字符串调用split(正则表达式),返回一个字符串数组,以正则表达式作为分隔符;
    13. 字符串解析、用Scanner(字符串)类来分隔字符串,Scanner对象调用useDelimiter(正则表达式)来设置分隔符,返回一个结果集,可以精细化的获取数字型的结果,和非数字型的结果,hasNext()判断,next/nextInt/nextDouble获取下一个
    14. 模式匹配、建立模式对象Pattern.compile(正则表达式),建立匹配对象 Pattern对象.matcher(目标字符串),返回结果集,Matcher对象调用方法find()寻找返回Boolean(类似next()+hasNext()),group()返回匹配的字符串,replaceAll(替换的字符串)返回一个替换了所以匹配的字串的新的字符串,模式可以用|来连接,即正则表达式1|正则表达式2
    15. date类与时间输入/输出格式、格式对象SimpleDateFormat(“‘beijingtime’:yyyy-MM-dd hh:mm:ss(a)(EE)”),年月日时分秒上午星期,SimpleDateFormat对象.format(new Date),设置当前的时间的输出格式,返回字符串;创建日历对象Calendar.getInstance(),翻日历Calendar.set (时间),获取时间毫秒Calendar.getTimeInMillis();Calendar.get(Calendar.YEAR)…(一月是0)
    16. List< V>,链,曾add(E),删remove(节点数)/ remove(节点元素)删除第一个,改set(节点数, 替换节点元素),查get(节点数),判断是否元素contains(元素)返回Boolean,克隆clone(),转为数组toArray(),遍历Iterator< V> It =List< V>.iterator();判断hasNext(),获取next()。
    17. ArrayList< T>,顺序结构
    18. Set< V>,HashSet< T>集合,曾add(E),删remove(节点元素)删除第一个,查判断是否元素contains(元素)返回Boolean,判断一个集合是否包含另一个集合containsAll(集合),克隆clone(),转为数组toArray(),遍历Iterator< V> It =List.iterator();判断hasNext(),获取next()。集合的并addAll(集合),交retainAll(集合),差removeAll(集合),Set< V>实现了Collection< E >
    19. Map<K,V>,键值对,曾put(K,V),删remove(K),查get(K),判断是否包含键containsKey(K)返回Boolean,判断是否包含值containsValue(V)返回Boolean,克隆clone(),转为数组toArray(),遍历Collection collection= Map<K,V>.values();
      Iterator< V> It = collection.iterator();排序键对象的类实现Conparator接口,实现比较大小的方法int compareTo(Object e)方法
    20. TreeMap,
    21. HashMap
    22. Stack< E>,栈,入栈push(),出栈pop();查search(元素对象)栈顶为1,没有元素为-1
    23. 线程(Thread\Runable\生命周期\线程同步\线程守护\线程联合),引用型,开始start(),自动调用方法run(),休眠sleep(毫秒),isAlive检测线程是否死亡,返回当前线程currentThread(),线程自己结束休眠interrupt();
    24. Runable是接口,要传入Thread(Runable );同步,用关键字synchronized修饰线程中的变量,方法,程序块;线程中的成员变量共享,局部变量私有;挂起wait();恢复notifyAll();联合B.join(),当前线程运行转为B线程;守护线程thread.setDaemon(true),当其他线程执行为,守护线程也要立刻停止执行,直接结束。
    25. 文件\目录(创建\运行\判断),创建file(文件路径),获取名字getName,判断文件是否存在exits(),获取父目录getParent(),判断是否是目录isDirectroy(),判断是否是文件isFile(),创建目录File.mkdir(),返回目录下的所以文件list()/listFiles(),创建文件File.createNewFile(),删除File.delete(),
    26. 运行可执行文件Runtime.getRuntime()然后Runtime.exec(cmd /k start 文件路径)
      File file = new File(“C:\WINDOWS\system32”, “notepad.exe e:\hello.txt”);// C:\WINDOWS\system32是编译器;notepad.exe记事本解释器;e:hello.txt绝对路径
      // exe.exec(file.toString());
      // 或者:exe.exec(“C:\WINDOWS\system32\notepad.exe e:\hello.txt”);
      exe.exec(“cmd /k start C:\Users\6688\Desktop\MV-少女时代-Gee-高清版-音扑网-587.mp4”);//可以运行文件// 打开一个word文档:exec(“Files\Microsoft Office\office\winword.exe \a.doc”);
    27. 输入\输出流(I\O){四类流},
    28. 文件字节流FileInputStream(file)类和FileOutputStream(file)类,读read(),写write();
    29. 文件字符流FileReader(file)类和FileWrite(file)类,读read(),写write();
    30. 缓冲流BufferReader(FileReader)类和BufferWrite(FileWrite) ,读readLine(),写write();
    31. 数组流ByteArrayOutputStream()和ByteArrayinputStream(byte[]),toByteArray();
    32. 字符串流StringReader(字符串)和StringWrite(),数据流DataInputStream(InputStream)和DataOutputStream(OutputStream),
    33. 对象流ObjectInputStream(InputStream)和ObjectOutputStream(OutputStream),对象要序列化,实现Serializable,读readObject(),写writeObject();
    34. 随机读写流RandomAccessFile(file,”rw”);String.getBytes(“utf-8”)返回utf-8编码的字节数组;
    35. 文件锁 流对象.getChannel()得到信道对象FileChannel,获取文件锁对象FileLock(加锁)FileChannel.tryLock,解锁:FileLock.release()
    36. 解析文件、File file=File(文件路径);Scanner sc=new Scanner(file); sc.useDelimiter(正则表达式{分隔符}),判断hasNext();取next
    37. UI(布局/组件/事件/读取多媒体对象),窗体JFrame,菜单组件{菜单条JMenuber、菜单JMenu、菜单项JMenultem},
    38. 布局FlowLayout、BorderLayout、CardLayout、GridLayout,
    39. 中间容器{面板JPanel、滚动窗格JScrollPane、拆分窗格JSpliPane、分层窗格JLayerdPane},文本组件{文本框JTextField、密码框JPasswordField、文本区JTextArea},按钮JButton,标签JLabel,复选框JCheckBox,单选框JRadioButton,列表组件JComboBox(),表格JTable,树组件JTree、进度条JProgressBer,
    40. 事件XXXlistener,计时器Timer(时间,事件),键盘绑定,对话框JDialog
    41. URL对象,URL(资源路径) URL.openStream获得资源输入流,进行资源的读取。
    42. java网络编程(套接制TCP-IP/多线程/UDP数据报/广播数据报/远程调用RMI),InetAddress类getByName(域名/ip地址),获得本地ip,getLocalHost();
    43. TCP/IP服务器端ServerSocket(端口);ServerSocket.accept()等待客户端的套接字连接请求返回一个客户端专属套接字Socket对象客户端Socket(服务器ip地址,端口);///流连接,当建立套接字连接后Socket对象可以使用getOutputStream()获得输出流和getInputStream()获得输入流,套接字的流数据区域两段对接;当没有数据发送将堵塞流,一直等待到有数据时开始运行,
    44. UDP发送数据创建DatagramPacket(字节数组,长度,InetAddtress对象,端口)对象打包数据即数据报对象,发送数据报创建DatagramSocket()对象,DatagramSocket.send(DatagramPacket);///创建接收数据报对象DatagramPacket(字节数组,长度) ,再创建DatagramSocket(端口)对象接收数据报,即DatagramSocket.receive(DatagramPacket),数据信息放入了字节数组内;
    45. 广播数据报首先设置组播地址对象InetAddress.getByName(IP地址);然后创建多点广播套接字对象MulticastSocket(端口号);然后设置广播范围MulticastSocket.setTimeToLive(范围int)经历路由器的最大个数;然后将发送端或者接收端加入组播MulticastSocket.joinGroup(InetAddress组播地址对象),离开组播MulticastSocket.leaveGroup(InetAddress组播地址对象);接收MulticastSocket.receive(DatagramPacket)和广播数据MulticastSocket.send(DatagramPacket);
    46. 远程调用(RMI)接口Remote
    47. 数据库操作(JDBC/增删改查/用结果集更新数据库/缓冲/预处理/事务处理/批处理/模糊查询/范围查询/复合查询/排序查询)。
    48. 模糊查询where name like ‘[张李]%’,% 0个或多个,_ 一个,排序查询Order by height,
    49. 随机查找rs.absolute(行号)看例子,用结果集更新数据库表更新rs.updateString(2,”王小二”)、插入rs.moveToInsertRow();rs.updateString(2,”王小二”);rs.insertRow() ;
    50. 缓冲创建CachedRowSetImpl对象,将rs放入CachedRowSetImpl中,CachedRowSetImpl.populate(rs);
    51. 预处理PreparedStatement,通配符? setXXX(1,”sss”),
    52. 事务Connection.setAutoCommit(false),关闭自动提交,提交事务Connection.commit();事务回滚Connection.rollback();
    53. 批处理Statement.addBatch(sql);添加sql语句,执行批处理Statement.executeBatch()。
    展开全文
  • 字符串多模式匹配:AC算法

    万次阅读 2017-03-21 19:17:22
    早在1975年贝尔实验室的两位研究人员Alfred V. Aho 和Margaret J. Corasick就提出了以他们的名字... AC算法是一个经典的多模式匹配算法,可以保证对于给定的长度为n的文本,和模式集合P{p1,p2,…pm},在O(n)时间复杂度

      早在1975年贝尔实验室的两位研究人员Alfred V. Aho 和Margaret J. Corasick就提出了以他们的名字命名的高效的匹配算法—AC算法。该算法几乎与《KMP算法》同时问世。与KMP算法相同,AC算法时至今日仍然在模式匹配领域被广泛应用。
      
      AC算法是一个经典的多模式匹配算法,可以保证对于给定的长度为n的文本,和模式集合P{p1,p2,…pm},在O(n)时间复杂度内,找到文本中的所有目标模式,而与模式集合的规模m无关。
      
      正如KMP算法在单模式匹配方面的突出贡献一样,AC算法对于多模式匹配算法后续的发展也产生了深远的影响,而且更为重要的是,两者都是在对同一问题——模式串前缀的自包含问题的研究中,产生出来的,AC算法从某种程度上可以说是KMP算法在多模式环境下的扩展。
      
      如果要用KMP算法匹配长度为n的文本中的m个模式,则需要为每一个模式维护一个next跳转表,在执行对文本的匹配过程中,我们需要关注所有这些next表的状态转移情况,这使得时间复杂度增长为O(mn),对于较大的模式集合来说,这样的时间增长可能是无法接受的。AC算法解决了这一问题,通过对模式集合P的预处理,去除了模式集合的规模对匹配算法速度的影响。
      

    AC定义:

    AC有限自动机 M 是1个6元组:M =(Q,∑,g,f,qo,F)其中:

    1、Q是有限状态集(模式树上的所有节点).
    2、∑是有限的输入字符表(模式树所有边上的字符).
    3、g是转移函数.
    4、f是失效函数,不匹配时自动机的状态转移.
    5、qo∈Q是初态(根节点);
    6、F量Q是终态集(以模式为标签的节点集).

    AC算法思想

      多模式匹配AC算法的核心仍然是寻找模式串内部规律,达到在每次失配时的高效跳转。这一点与单模式匹配KMP算法和BM算法是一致的。不同的是,AC算法寻找的是模式串之间的相同前缀关系。
      
      要理解AC算法,仍然需要对KMP算法的透彻理解。那么前缀自包含如何在AC算法中发挥作用?
      
      在KMP算法中,对于模式串”abcabcacab”,我们知道非前缀子串abc(abca)cab是模式串的一个前缀(abca)bcacab,而非前缀子串ab(cabca)cab不是模式串abcabcacab的前缀,根据此点,我们构造了next结构,实现在匹配失败时的跳转。
      
      而在多模式环境中,这个情况会发生一定的变化。
      对于模式集合P{he,she,his,hers},模式s(he)的非前缀子串he,实际上却是模式(he),(he)rs的前缀。如果目标串target[i…i+2]与模式she匹配,同时也意味着target[i+1…i+2]与he,hers这两个模式的头两个字符匹配,所以此时对于target[i+3],我们不需要回溯目标串的当前位置,而直接将其与he,hers两个模式的第3个字符对齐,然后直接向后继续执行匹配操作
      
      经典的AC算法由三部分构成,goto表,fail表和output表,共包含四种具体的算法,分别是计算三张查找表的算法以及AC算法本身。
      goto表是由模式集合P中的所有模式构成的状态转移自动机。
      failure表作用是在goto表中匹配失败后状态跳转的依据,这点与KMP中next表的作用相似。
      output表示输出,又称:emits,即代表到达某个状态后某个模式串匹配成功

    构造goto表

      goto表是由模式集合P中的所有模式构成的状态转移自动机,本质上是一个有限状态机,这里称作模式匹配机(Pattern Matching Machine,PMM)。
      
      对于给定的集合P{p1,p2,…pm},构建goto表的步骤是,对于P中的每一个模式pi[1…j](1 <= i < m+1)),按照其包含的字母从前到后依次输入自动机,起始状态D[0],如果自动机的当前状态D[p],对于pi中的当前字母pi[k](1<=k<=j),没有可用的转移,则将状态机的总状态数smax+1,并将当前状态输入pi[k]后的转移位置,置为D[p][pi[k]] = smax,如果存在可用的转移方案D[p][pi[k]]=q,则转移到状态D[q],同时取出模式串的下一个字母pi[k+1],继续进行上面的判断过程。这里我们所说的没有可用的转移方案,等同于转移到状态机D的初始状态D[0],即对于自动机状态D[p],输入字符pi[k],有D[p][pi[k]]=0。
      
      对于模式集合P{he,she,his,hers}, goto表的构建过程如下:
      
      1、PMM初始状态为0,然后向PMM中加入第一个模式串K[0] = “he”。
      
        
      
      2、继续向PMM中添加第二个模式串K[1] = “she”,每次添加都是从状态0开始扫描。
      
        
      
      3、从状态0开始继续添加第三个模式串K[2] = “his”,这里值得注意的是遇到相同字符跳转时要重复利用以前已经生成的跳转。如这里的’h’在第一步中已经存在。
      
        
      
      4、添加模式串K[3] = “hers”。至此,goto表已经构造完成。
      
        

      对于第一和第二步而言,两个模式没有重叠的前缀部分,所以每输入一个字符,都对应一个新状态。第三步时,我们发现,D[0][p3[1]]=D[0][‘h’]=1,所以对于新模式p3的首字母’h’,我们不需要新增加一个状态,而只需将D的当前状态转移到D[1]即可。而对于模式p4其前两个字符he使状态机转移至状态D[2],所以其第三字符对应的状态D[8]就紧跟在D[2]之后。

    构造failure表

      failure表作用是在goto表中匹配失败后状态跳转的依据,这点与KMP中next表的作用相似。
      
      首先说明什么状态,在上面goto表的图里,把圆圈里的数字记为状态。
      
      再引入状态深度的概念,状态s的深度depth(s)定义为在goto表中从起始状态0到状态s的最短路径长度。如goto表中状态1和3的深度为1。
      
      计算思路:先计算所有深度是1的状态的失效函数值,然后计算所有深度为2的状态,以此类推,直到所有状态(除了状态0,因为它的失效函数没有定义)的失效函数值都被计算出。

      计算方法:用于计算某个状态失效函数值的算法在概念上是非常简单的。首先,令所有深度为1的状态s的函数值为f(s) = 0。假设所有深度小于d的状态的f值都已经被算出了,那么深度为d的状态的失效函数值将根据深度小于d的状态的失效函数值来计算。
     
      具体步骤: 构造failure表利用到了递归的思想。

    1、若depth(s) = 1,则f(s) = 0;即与状态0距离为1(即深度为1)的所有状态的fail值都为0
    2、假设深度为d-1的所有状态r, 即depth(r) < d,已经计算出了f(r);
    3、那么对于深度为d的状态s:
      (1) 若所有的字符a, 满足g(r,a) = fail,则不动作;(注:g为状态转移函数)
      (2) 否则,对每个使 g(r, a) = s成立的字符a,执行以下操作::
         a、使state = f(r)
         b、重复步骤state = f(state),直到g(state, a) != fail。(注意对于任意的a,状态0的g(0,a) != fail)
         c、使f(s) = g(state, a)。

      例子: 求状态4 的failure 状态,已知其前一个(父节点)的f(1)= 0,且状态0(根节点)有字符’h’的外向边,该外向边对应状态1,则有f(4) = 1;类似前缀规则:求已经匹配字串”sh” 最大后缀,同时是某个模式串的前缀;

      根据以上算法,得到该例子的failure表为:
      i  1  2  3   4   5  6  7  8  9
      f(i) 0  0  0   1   2  0  3  0  3
       
      将failure表用虚线表现,整合goto表,得到下图:
        

    构造output表

      output表示输出,即代表到达某个状态后某个模式串匹配成功。该表的构造过程融合在goto表和failure表的构造过程中。
      
       1、在构造goto表时,每个模式串结束的状态都加入到output表中,也就goto表中的黑色加粗圆圈。得到
         i    output(i)
         2   {he}
         5   {she}
         7   {his}
         9   {hers}
       2、在构造failure表时,若f(s) = s’,则将s和s‘对应的output集合求并集。如f(5) = 2,则得到最终的output表为:
          i   output(i)
          2   {he}
          5   {she,he}
          7   {his}
          9   {hers}

    AC算法匹配过程

    这里写图片描述
      
       自动机从根节点0出发
       1、首先尝试按success表转移(图中实线)。按照文本的指示转移,也就是接收一个u。此时success表中并没有相应路线,转移失败。
       2、失败了则按照failure表回去(图中虚线)。按照文本指示,这次接收一个s,转移到状态3。
       3、成功了继续按success表转移,直到失败跳转步骤2,或者遇到output表中标明的“可输出状态”(图中红色状态)。此时输出匹配到的模式串,然后将此状态视作普通的状态继续转移。

       根据上面已经构造好的goto、failure和output表。以字符串”ushers”为例。状态转移如下:
            u   s   h   e   r   s
         0  0   3   4  5  8   9
                        2
       说明:在状态5发生失配,查找failure表,转到状态2继续比较。在状态5和状态9有输出。
      
       算法高效之处在于,当自动机接受了“ushe”之后,再接受一个r会导致无法按照success表转移,此时自动机会聪明地按照failure表转移到2号状态,并经过几次转移后输出“hers”。来到2号状态的路不止一条,从根节点一路往下,“h→e”也可以到达。而这个“he”恰好是“ushe”的结尾,状态机就仿佛是压根就没失败过(没有接受r),也没有接受过中间的字符“us”,直接就从初始状态按照“he”的路径走过来一样(到达同一节点,状态完全相同)。

    AC算法具体实现:

    robert-bor实现的ac算法(java):https://github.com/robert-bor/aho-corasick
    hankcs改进robert-bor的ac算法(java):https://github.com/hankcs/aho-corasick

    简单实现java:

    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.Collections;
    import java.util.Hashtable;
    import java.util.List;
    
    public class ACTest {
    
        static String Text = "ushers";
        static String[] Pattens = {"he", "she", "his", "hers"};
        // 根节点
        static TreeNode root; 
    
        public static void main(String[] args) {
            buildGotoTree(); // 构建goto表和output表
            addFailure();    // 构建failure表
            printTree();     // 打印tree的字符、深度、状态,fail值
            acSearch();      // 查找ushers, 并打印匹配的位置和模式
        }
    
        /**
         * 构建Goto表和output表
         */
        public static void buildGotoTree(){
            int i = 1;
            root = new TreeNode(null, ' ');
            // 判断节点是否存在, 存在转移 ,不存在添加   
            for (String word : Pattens) { 
                TreeNode temp = root;
                for (char ch : word.toCharArray()) {  
                    TreeNode innerTem = temp.getSonNode(ch);
                    if(innerTem == null){
                        TreeNode newNode = new TreeNode(temp, ch); 
                        newNode.setStatus(i++);
                        temp.addSonNode(newNode);  
                        innerTem = newNode;  
                    }
                    temp = innerTem;
                }  
                temp.addResult(word);
            }  
        }
    
        /**
         * 构建failure表
         * 遍历所有节点, 设置失败节点 原则: 节点的失败指针在父节点的失败指针的子节点中查找 最大后缀匹
         */
        public static void addFailure(){
            // 过程容器  
            ArrayList<TreeNode> mid = new ArrayList<TreeNode>();
    
            // 首先,设置二层失败指针为根节点 收集三层节点。 即令所有深度为1的状态s的函数值为f(s) = 0。 
            for (TreeNode node : root.getSons()) {  
                node.setFailure(root);  
                for (TreeNode treeNode : node.getSons()) {  
                    mid.add(treeNode);  
                }  
            }  
    
            // 广度遍历所有节点设置失败指针 1.存在失败指针 2.不存在到root结束  
            while(mid.size() > 0){  
                // 子节点收集器
                ArrayList<TreeNode> temp = new ArrayList<TreeNode>();  
                for (TreeNode node : mid) {  
                    TreeNode r = node.getParent().getFailure();  
                    // 没有找到,保证最大后缀 (最后一个节点字符相同)  
                    while(r != null && r.getSonNode(node.getCh()) == null){  
                        r = r.getFailure();
                    }  
    
                    // 是根结节点
                    if(r == null){  
                        node.setFailure(root);  
                    }else{  
                        node.setFailure(r.getSonNode(node.getCh()));  
                        // 重叠后缀的包含  
                        for (String result : node.getFailure().getResults()) {  
                            node.addResult(result);  
                        }  
                    }  
                    // 收集子节点  
                    temp.addAll(node.getSons());  
                }  
                mid = temp;  
            }  
        }
    
        /**
         * 根据状态顺序打印树信息
         */
        public static void printTree(){
            // 收集所有节点
            List<TreeNode> nodesList = new ArrayList<TreeNode>();
            // 过程容器 
            List<TreeNode> nodes = Arrays.asList(root);
            while(nodes.size() > 0){
                ArrayList<TreeNode> temp = new ArrayList<TreeNode>();  
                for(TreeNode node : nodes){
                    temp.addAll(node.getSons());
                    nodesList.add(node);
                }
                nodes = temp;
            }
            // 排序
            Collections.sort(nodesList, (a, b) -> a.getStatus().compareTo(b.getStatus()));
            for(TreeNode node : nodesList){
                System.out.println(node.getCh() + " " + node.getDepth() + " " + node.getStatus() + 
                        " " + (node.getFailure() != null ? node.getFailure().getStatus() : "0"));
            }
        }
    
        // 查找全部的模式串  
        public static void acSearch(){
            // 可以找到 转移到下个节点 不能找到在失败指针节点中查找直到为root节点  
            int index = 0;  
            TreeNode mid = root;  
            while(index < Text.length()){  
                TreeNode temp = null;  
    
                while(temp ==null){  
                    temp = mid.getSonNode(Text.charAt(index));  
                    if(mid ==root){  
                        break;  
                    }  
                    if(temp==null){  
                        mid = mid.getFailure();  
                    }  
                }  
                // mid为root 再次进入循环 不需要处理  或者 temp不为空找到节点 节点位移  
                if (temp != null) mid = temp;
    
                for (String result : mid.getResults()) {  
                    System.out.println((index - result.length() + 1) + ":" + index + "=" + result);
                }  
                index++;  
            }  
        } 
    }
    
    class TreeNode{
        // 父节点
        private TreeNode parent;
        // failuere
        private TreeNode failure;
        // 字符
        private char ch;
        // goto表
        private List<TreeNode> sons;
        // 获取子节点
        private Hashtable<Character, TreeNode> sonsHash;  
        // output
        private List<String> results;
        // 深度
        private int depth = 0;
        // 状态
        private Integer status = 0;
    
        public TreeNode(TreeNode parent, char ch) {  
            this.parent = parent;  
            this.ch = ch;  
            results = new ArrayList<String>();  
            sonsHash = new Hashtable<Character, TreeNode>();  
            sons = new ArrayList<TreeNode>();
            if(parent != null)
                depth = parent.getDepth() + 1;
        }  
    
        // 添加一个结果到结果字符中, 状态5量 output : he 和 she
        public void addResult(String result){  
              if(!results.contains(result)) results.add(result); 
        }
    
        // 添加子节点  
        public void addSonNode(TreeNode node){  
            sonsHash.put(node.ch, node);  
            sons.add(node);
        }  
    
        // 设置失败指针并且返回  
        public TreeNode setFailure(TreeNode failure){  
            this.failure = failure;  
            return this.failure;  
        }  
    
        // 获取子节点中指定字符节点  
        public TreeNode getSonNode(char ch){  
            return sonsHash.get(ch);  
        }  
    
        // 获取父节点
        public TreeNode getParent() {
            return parent;
        }
        // 获取字符
        public char getCh() {
            return ch;
        }
        // 获取所有的孩子节点  
        public List<TreeNode> getSons() {
            return sons;
        }
        // 获取搜索的字符串  
        public List<String> getResults() {
            return results;
        }
        // 获取深度
        public int getDepth() {
            return depth;
        }
        public Integer getStatus() {
            return status;
        }
        public void setStatus(Integer status) {
            this.status = status;
        }
        public TreeNode getFailure() {
            return failure;
        }
    }

    心得:

      KMP算法依然是解读AC算法的重要线索,前缀,子串,后缀永远和模式匹配纠缠在一起。
      
      AC状态机实际上更适合用Trie结构来存储。
      
      可以将算法中使用到的goto,fail,output三张表以离线的方式计算出来保存在一个文件中,当AC算法启动时,直接从文件中读取三个表的内容,这样可以有效减少每次AC算法启动时都需要构建三个表所花费的时间。
      
      可以把同深度节点排序,后面查找某状态的指定字符外向边状态,可以使用二分查找,加快速度;

      值得注意的是在AC算法的以上实现中,对于输入字符a的跳转次数是不确定的。因为有可能在输入a后发生失配,需要查找failure表重新跳转。能不能在PMM的基础上实现确定型的有限状态机,即对于任意字符a,都只用进行一次确定的状态跳转?答案是肯定的。在Aho和Corasick论文中给出了处理的方法:构造一个与KMP算法中相似的next表,实现确定性跳转。

    展开全文
  • Java 模式匹配find vs matches

    千次阅读 2017-09-11 16:52:33
    Java模式匹配中,find与matches有什么区别呢? find()倾向于搜索,部分匹配。只要部分找到满足正则表达式即可。 matches()倾向于字符串的完整匹配,部分满足条件不可行。 看下面的例子: public void test1()...
  • Java正则表达式匹配多行

    万次阅读 2014-09-25 20:22:46
    只能匹配出\n以外的字符,如果遇到要匹配的字符串包含回车换行符(多行),则正则表达式遇到换行符后会停止,导致包含回车换行符的串不能正确匹配,解决的办法是: 1、使用Pattern和Matcher对象 设置Pattern模式为...
  • Java 使用正则表达式匹配淘口令

    千次阅读 2019-07-17 10:16:27
    项目中被正则表达式的反斜线问题坑了几次了,今天恰好用到正则表达式的匹配,又遇到饭斜线的处理,记录一下。先对比其他语言和 Java 语言中反斜线,最后再给出淘口令匹配的案例。
  • Java 正则表达式 SQL匹配

    千次阅读 2018-09-19 20:33:09
    非贪婪模式尽可能少的匹配所搜索的字符串,而默认的贪婪模式则尽可能多的匹配所搜索的字符串。例如,对于字符串“oooo”,“o+?”将匹配单个“o”,而“o+”将匹配所有“o”。 .点 ...
  • print("你今天的随机数字是:",random.choice(range(10)))#输出在0-9之间随机选择的整数 input() 在桌面打开PowerShell(还有两种输入方式:python hello.py或者.\hello.py) 或者在桌面打开cmd, 就输入hello.py或者...
  • 串-定义和模式匹配算法

    千次阅读 2017-03-12 21:38:59
    5.2 串的定义 今天我们就是来研究"串"这样的数据结构。...所以在计算机的运算当中,模式匹配操作可说是随处可见,而刚才的这个算法,就显得太低效了。 引用《大话数据结构》作者:程杰
  •  //find()尝试查找与该模式匹配的输入序列的下一个子序列   System.out.println(m.group(0));   operatorStr.replace(m.start(), m.end(), "");   System.out.println(operatorStr);   m =...
  • C#语言

    千次阅读 2019-11-08 10:54:13
    ​ 组成:52个英文字母(AZ,az)、10个数字(0~9)、下划线(_),除此之外不能含有其他的字符。 ​ 开头:只能以字母或下划线开头。 ​ 不能使用的:不能是C#中的关键字。 3、C#中的常量 ​ 常量...
  • Linux总结

    千次阅读 多人点赞 2020-01-14 20:36:45
    ) i,a: 命令模式–>编辑模式 ecs: 编辑模式–>命令模式(底行模式) 命令模式(底行模式下): q!: 强制退出 q: 退出 wq: 保存并退出 (wq以及wq!,则不管有没有修改,都会强制更新修改时间,有时没修改的东西会让人误...
  • java字符串提取数字

    千次阅读 2018-09-14 17:08:52
    /** * 提取字符串中的数字 * @param strInput * @return */ public static String getNum(String strInput) { //匹配指定范围内的数字 String regEx = "[^0-9]"; //Pattern是一个正则表达式经...
  • 你想太多了,部署时候基本上只有webapp里的会直接输出到根目录,其他都会放入WEB-INF里面,项目内部依然可以使用classpath:XXX来访问,好像IDE里可以设置部署输出目录,这里扯远了~ - test 这里是测试分支。...
  • 前端面试题

    万次阅读 多人点赞 2019-08-08 11:49:01
    前端面试题汇总 ... 你做的页面在哪些流览器测试过?这些浏览器的内核分别是什么?...它和Standards模式有什么区别 21 div+css的布局较table布局有什么优点? 22 img的alt与title有何异同? strong与em的异同? 22 你能...
  • java 字符串模糊匹配

    万次阅读 2014-10-17 17:45:03
    Pattern类用于创建一个正则表达式,也可以说创建一个匹配模式,它的构造方法是私有的,不可以直接创建,但可以通过Pattern.complie(String regex)简单工厂方法创建一个正则表达式, Java代码示例: Pattern p=Pattern....
  • 测试开发笔记

    万次阅读 多人点赞 2019-11-14 17:11:58
    测试开发笔记 第一章 测试基础 7 什么是软件测试: 7 ★软件测试的目的、意义:(怎么做好软件测试) 7 3.软件生命周期: 7 ...5.各阶段输入、输出标准以及入口、出口准则:(测试阶段过程要素) 1...
  • Java正则表达式获得字符串中数字

    万次阅读 2016-05-06 21:56:18
    下面通过一个小范例来学习如何获得一个字符串中的数字import java.util.regex.Matcher; import java.util.regex.Pattern; public class test { public static void main(String[] args) { String strInput = "3a7s...
  • 浦发银行 信息科技岗 大数据方向 面经

    万次阅读 多人点赞 2018-08-09 23:00:31
    机试题目都很简单一共三道题目:①输入一个数字要求输出数字各个位上偶数的和,如输入5584,输出12。②输入一组数N和数字b ,求出该组数字中能被b 整除的个数。如输入1 2 3 4 5 6和 2,结果输出为3。③求N阶楼梯...
  • Java 字符串 正则表达式

    千次阅读 2018-10-31 11:29:59
    字符串 String类:最常用的字符串。初始化后不可改变,String变量的重新赋值其实是重新分配内存。 StringBuffer类:多线程环境中,可变...http://www.runoob.com/java/java-regular-expressions.html?yyue=a21bo.5...
  • C#基础教程-c#实例教程,适合初学者

    万次阅读 多人点赞 2016-08-22 11:13:24
    C#语法和C++和JAVA语法非常相似,如果读者用过C++和JAVA,学习C#语言应是比较轻松的。 用C#语言编写的源程序,必须用C#语言编译器将C#源程序编译为中间语言(MicroSoft Intermediate Language,MSIL)代码,形成扩展名...
  • java正则表达式解析

    万次阅读 多人点赞 2018-11-08 20:59:07
    一、知道java正则表达式是干什么的? 百度百科定义: 其实这已经说得很明确了,正则表达式其实就是一个字符串,这个字符串是按照一定的规则进行组合得来的,而这个规则当然是创始者定义,用这些规则我们能做什么...
  • java控制台输出图书馆管理系统(只用java代码不用数据库和GUI,java入门的新手秒懂) 在个项目中,我只用数组保存数据,和只用for循环和if条件语句来完成,虽然比较局限,但可以让新手快速体验一下做小项目的乐趣,...
  • java格式化数字 NumberFormat及DecimalFormat

    万次阅读 多人点赞 2019-05-06 17:34:22
    前言 以前用到要对数字格式的地方,都是直接到网上搜一下。拿过来能用就行。因为平时用的不多。...你会发现java对文字,数字的格式化,是有一个公共的父类的Format。 NumberFormat和DecimalFormat都是它...
  • 4.java字符串和输入输出

    万次阅读 2018-11-09 01:50:36
    目录 1.String类 2.StringBuilder类 ...6.java格式化输出 1.String类 从概念上讲,java字符串就是Unicode字符序列。​​​​ String类提供处理Unicode代码点(即字符)的方法,以及用于处理...
  • HBuilder 使用教程

    千次阅读 2016-11-15 09:49:01
    HBuilder是DCloud(数字天堂)推出的一款支持HTML5的Web开发IDE。HBuilder的编写用到了Java、C、Web和Ruby。HBuilder本身主体是由Java编写,它基于Eclipse,所以顺其自然地兼容了Eclipse的插件。快,是HBuilder的...
  • 原文引自:http://download.oracle.com/technetwork/java/javase/6/docs/zh/api/java/text/DecimalFormat.html java.text  类 DecimalFormat java.lang.Object java.text.Format java.text.NumberFormat
  • java中XML 数字签名的标准

    千次阅读 2016-03-23 16:29:01
    数字签名是非对称密钥技术的一种应用模式,用于保证报文的完整性,不可否认性,以及提供身份认证信息。数字签名的原理如图 1 所示。 图 1:数字签名的原理 发送者在发送报文之前,先选用某种摘要算法为报文生成...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 54,533
精华内容 21,813
关键字:

java输出模式匹配的数字

java 订阅