I'm the type of person that if you ask me a question and I don't know the answer, I'm gonna tell you that I don't know. But I bet you what, I know how to find the answer and I will find the answer.
classKMP{privatestaticfinalStringALPHABETS="abc";privateint[][]initDfa(Stringpat){intm=pat.length();intr=KMP.ALPHABETS.length();int[][]dfa=newint[r][m];// init with 0dfa[pat.charAt(0)-'a'][0]=1;// init first columnintx=0;// previous statefor(inti=1;i<pat.length();i++){for(intj=0;j<r;j++){// copy x state to currentdfa[j][i]=dfa[j][x];}intci=pat.charAt(i)-'a';dfa[ci][i]=i+1;// machedx=dfa[ci][x];// update x}returndfa;}publicintsearch(Stringpat,Stringstr){int[][]dfa=this.initDfa(pat);intj=0;for(inti=0;i<str.length();i++){charc=str.charAt(i);j=dfa[c-'a'][j];if(j==pat.length()){returni-j+1;}}return-1;}}
算法里的 x 是比较重要的, 按照之前的描述我们是需要每次都回溯的, 但是因为 Pat 的字符串是不变的,
也就是说, 每次回溯时前面的部分都是一样的, 所以用 x 记录一下省得每次都回溯.
jump=[]defpratt(pat,s):m=len(pat)ifm==0:return0j=0i=0forcins:whilej>0andc!=pat[j]:j=jump[j]ifc==pat[j]:i+=1j+=1else:# j == 0 and c != pat[j]
i+=1ifj==m:returni-jreturn-1
完整的 BM 算法实现起来比较麻烦, BMH 是它的简化版本, 不过匹配起来依旧很快.
BMH 是根据当前不匹配的字符来决定pat 的位移的. 比如说, 我们从右向左的匹配, 匹配第一个字符就失败了,
同时这个字符也没有在 pat 中出现过, 那么我们可以放心大胆的将 pat 直接向右位移 len(pat) 个距离.
可以看到决定位移的距离的关键在于当前不匹配的那个字符是否在 pat 中出现过, 出现的位置是多少.
这就是所谓的坏字符表.
坏字符表
我们用一个字典来表示坏字符表, 每一个在 pat 中出现的字符都在这个字典里, 对应的值为这个字符在 pat
中最后一次出现的位置. 比如 pat = 'aba' 那么坏字符表就等于 {'a': 2, 'b': 1}
我们先看假如我们现在有了坏字符表, 我们应该怎么匹配呢?
bad={}defBMH(s,pat):m=len(pat)n=len(s)i=m-1j=m-1whilei<nandj>=0:b=s[i]ifb!=pat[j]:ifbinbad:inc=m-1-bad[b]inc=m-jifinc<m-jelseinc# 在反向 BF 中 i 至少会增加 m - j
else:inc=mi+=incj=m-1else:i-=1j-=1returni+1ifj==-1else-1
In general, the pattern at the end might appear elsewhere, so we need an array of restart positions as for Knuth-Morris-Pratt. We will not explore this approach in further detail because it is quite similar to our implementation of the Knuth-Morris-Pratt method.
就这么一笔带过了! 果然境界不一样啊.
这里我也不打算详细的叙述 (这文章已经断断续续的写了好多天了), 我就贴个代码好了:
defBM(s,pat):n=len(s)m=len(pat)jump=self.initJump(self.initBorder(pat))i=m-1j=m-1whilei<nandj>=0:ifs[i]!=pat[j]:i+=jump[j]j=m-1else:i-=1j-=1returni+1ifj==-1else-1definitBorder(pat):m=len(pat)border=[m]*m# last one will always equal to `m`
forjinrange(m-1)[::-1]:prev=border[j+1]whileprev!=m:ifpat[j]==pat[prev-1]:border[j]=prev-1breakprev=border[prev]else:border[j]=m-1ifpat[j]==pat[m-1]elsemreturnborderdefinitJump(border):m=len(border)ifm==0:return[]jump=[0]*mjump[m-1]=1j=border[0]foriinrange(m):b=border[i]ifb!=m:jump[b-1]=m-iifjump[i]!=0:continuewhilei>=j:j=border[j]jump[i]=j+m-i-1returnjump
Index: A MotionEvent effectively stores information about each pointer in an array. The index of a pointer is its position within this array. Most of the MotionEvent methods you use to interact with pointers take the pointer index as a parameter, not the pointer ID.
ID: Each pointer also has an ID mapping that stays persistent across touch events to allow tracking an individual pointer across the entire gesture.
When multiple pointers touch the screen at the same time, the system generates the following touch events:
ACTION_DOWN—For the first pointer that touches the screen. This starts the gesture. The pointer data for this pointer is always at index 0 in the MotionEvent.
ACTION_POINTER_DOWN—For extra pointers that enter the screen beyond the first. The pointer data for this pointer is at the index returned by getActionIndex().
ACTION_MOVE—A change has happened during a press gesture.
ACTION_POINTER_UP—Sent when a non-primary pointer goes up.
ACTION_UP—Sent when the last pointer leaves the screen.
ACTION_MOVE
当你多个手指在屏幕上移动的时候,
只有 Index 为 0 的那个 Pointer 会触发 ACTION_MOVE 的事件.
ACTION_POINTER_UP
文档里说, 当 non-primary 的 Pointer 离开时会触发该事件,
那么什么叫 non-primary? 我一开始认为非第一个触摸的手指就是 non-primary 的,
后来看到 Android 官方博客里的 Making Sense of Multitouch
这篇文章, 里面写到:
If there is already a pointer on the screen and a new one goes down, you will receive ACTION_POINTER_DOWN instead of ACTION_DOWN. If a pointer goes up but there is still at least one touching the screen, you will receive ACTION_POINTER_UP instead of ACTION_UP.
This time, we show the socket send buffer as a dashed box because it doesn’t really
exist. A UDP socket has a send buffer size (which we can change with the SO_SNDBUF
socket option, Section 7.5), but this is simply an upper limit on the maximum-sized
UDP datagram that can be written to the socket. If an application writes a datagram
larger than the socket send buffer size, EMSGSIZE is returned.