From 166cd2737bc7a7949b91a59d7d2e864731377afd Mon Sep 17 00:00:00 2001 From: mbinary Date: Tue, 11 Dec 2018 15:28:05 +0800 Subject: [PATCH] String matching algorithm, permutation algorithm --- dataStructure/intervalTree.py | 15 ++- dataStructure/redBlackTree.py | 1 + divideAndConquer/min_distance_of_n_points.py | 8 +- dynamicProgramming/lcs.hs | 8 -- dynamicProgramming/lcs.py | 23 ++-- math/permute_back_track.py | 12 ++ math/{cantor.c => permute_cantor.c} | 20 +++- math/permute_divide_and_conquer.py | 12 ++ ...rangement.c => permute_next_arrangement.c} | 0 math/primesLEn.hs | 3 + search/BFS_knight.hs | 34 ++++++ search/binary_search.hs | 7 ++ search/schedule.py | 104 +++++++++++++++++ string/KMP.py | 58 +++++++++ string/README.md | 110 ++++++++++++++++++ string/rabin_karp.py | 60 ++++++++++ string/src/compare.jpg | Bin 0 -> 11923 bytes string/src/general.jpg | Bin 0 -> 20265 bytes string/sunday.py | 40 +++++-- 19 files changed, 479 insertions(+), 36 deletions(-) delete mode 100644 dynamicProgramming/lcs.hs create mode 100644 math/permute_back_track.py rename math/{cantor.c => permute_cantor.c} (72%) create mode 100644 math/permute_divide_and_conquer.py rename math/{arrangement.c => permute_next_arrangement.c} (100%) create mode 100644 math/primesLEn.hs create mode 100644 search/BFS_knight.hs create mode 100644 search/binary_search.hs create mode 100644 search/schedule.py create mode 100644 string/KMP.py create mode 100644 string/README.md create mode 100644 string/rabin_karp.py create mode 100644 string/src/compare.jpg create mode 100644 string/src/general.jpg diff --git a/dataStructure/intervalTree.py b/dataStructure/intervalTree.py index 1f3eb85..9ad88a9 100644 --- a/dataStructure/intervalTree.py +++ b/dataStructure/intervalTree.py @@ -84,7 +84,7 @@ def genNum(n =10,upper=10): return nums.values() def buildTree(n=10,nums=None,visitor=None): - if nums is None or nums ==[]: nums = genNum(n) + #if nums is None or nums ==[]: nums = genNum(n) tree = intervalTree() print(f'build a red-black tree using {nums}') for i in nums: @@ -100,6 +100,7 @@ def testInsert(nums=None): print('-'*5+ 'in-order visit' + '-'*5) for i,j in enumerate(tree.sort()): print(f'{i+1}: {j}') + return tree def testSuc(nums=None): tree,nums = buildTree(nums=nums) @@ -113,10 +114,16 @@ def testDelete(nums=None): print(f'deleting {i}') tree.delete(i[0]) print(tree) + return tree if __name__=='__main__': lst = [(0,3),(5,8),(6,10),(26,26),(25,30),(8,9),(19,20),(15,23),(16,21),(17,19)] - lst = None + #lst = None #testSuc(lst) - #testInsert(lst) - testDelete(lst) + tree = testInsert(lst) + #tree,_= buildTree(lst) + while 1: + a =int( input('low:')) + b =int( input('high:')) + res = tree.search(a,b) + print(res) diff --git a/dataStructure/redBlackTree.py b/dataStructure/redBlackTree.py index afb3e36..dec1447 100644 --- a/dataStructure/redBlackTree.py +++ b/dataStructure/redBlackTree.py @@ -286,6 +286,7 @@ def buildTree(n=10,nums=None,visitor=None): print(f'build a red-black tree using {nums}') for i in nums: rbtree.insert(node(i)) + print(rbtree) if visitor: visitor(rbtree,i) return rbtree,nums diff --git a/divideAndConquer/min_distance_of_n_points.py b/divideAndConquer/min_distance_of_n_points.py index ea1da6f..858c328 100644 --- a/divideAndConquer/min_distance_of_n_points.py +++ b/divideAndConquer/min_distance_of_n_points.py @@ -107,18 +107,18 @@ def test(f=minDistance_n2): print('result: {:.2f} {} {}\n'.format(minD, p,q)) def genData(n,unique=True): + upper = 1000000 if unique: points = set() for i in range(n): - points.add(point(randint(1,1000),randint(1,1000))) + points.add(point(randint(1,upper),randint(1,upper))) return list(points) - else:return [point(randint(1,1000),randint(1,1000)) for i in range(n)] + else:return [point(randint(1,upper),randint(1,upper)) for i in range(n)] if __name__ =='__main__': - n = 10000 + n = 1000 points = genData(n, unique=True) print('min distance of {} points'.format(n)) #print(sorted(points)) test(minDistance_n2) test(minDistance_nlogn) - diff --git a/dynamicProgramming/lcs.hs b/dynamicProgramming/lcs.hs deleted file mode 100644 index 30bbf8a..0000000 --- a/dynamicProgramming/lcs.hs +++ /dev/null @@ -1,8 +0,0 @@ -import Vec2d (Vec2d,getVal,setVal) - - -lcs a b = - let m = lenghth a - n = length b - rst = [] - in 1 --to do diff --git a/dynamicProgramming/lcs.py b/dynamicProgramming/lcs.py index 8cb70a2..9303ac9 100644 --- a/dynamicProgramming/lcs.py +++ b/dynamicProgramming/lcs.py @@ -29,17 +29,22 @@ def lcs2(a,b): m,n= len(a),len(b) board = [[] for i in range(n+1)] for i in range(m): - last = [] + upperLevel = board[0].copy() for j in range(n): + tmp = board[j+1].copy() if a[i]==b[j]: - board[j+1] =board[j]+[a[i]] - elif len(board[j+1]) < len(last): - board[j+1] = last - last = board[j+1] + board[j+1] = upperLevel+[a[i]] + elif len(board[j+1]) < len(board[j]): + board[j+1] = board[j].copy() # copy is needed + upperLevel = tmp return board[n] if __name__ =='__main__': - a="dsaffqewqfqewregqwefqwe" - b="adsfsfs3qt5yhyh24efwq" - print(lcs(a,b)) - print(lcs2(a,b)) + a = 'ABCBDAB' + b = 'BDCABA' + print('s1:',a) + print('s2:',b) + while 1: + print('lcs:',lcs2(a,b)) + a = input('s1: ') + b = input('s2: ') diff --git a/math/permute_back_track.py b/math/permute_back_track.py new file mode 100644 index 0000000..16547ba --- /dev/null +++ b/math/permute_back_track.py @@ -0,0 +1,12 @@ +def permute(n): + def _util(lst,i): + if i==n:print(lst) + else: + for j in range(i,n): + lst[i],lst[j]=lst[j],lst[i] + _util(lst,i+1) + lst[i],lst[j]=lst[j],lst[i] + _util([i for i in range(n)],0) + +if __name__=='__main__': + permute(5) diff --git a/math/cantor.c b/math/permute_cantor.c similarity index 72% rename from math/cantor.c rename to math/permute_cantor.c index 18b412a..b0da38c 100644 --- a/math/cantor.c +++ b/math/permute_cantor.c @@ -11,7 +11,7 @@ void calFac(int n) } } -void getArrangement(int *arr,int n,int sum) +void permute(int *arr,int n,int sum) { /*sum表示全排列由小到大排序后的名次,从0 开始计数, 由名次求出 n位的排列存储到 arr 中*/ int i,j,ct=0,k, ct2; @@ -36,3 +36,21 @@ void getArrangement(int *arr,int n,int sum) } } +void printArr(int *p,int n) +{ + for(int i=0;i mod n x /=0) li then n:li else li diff --git a/search/BFS_knight.hs b/search/BFS_knight.hs new file mode 100644 index 0000000..ba6ba9c --- /dev/null +++ b/search/BFS_knight.hs @@ -0,0 +1,34 @@ +{- mbinary +######################################################################### +# File : BFS_knight.hs +# Author: mbinary +# Mail: zhuheqin1@gmail.com +# Blog: https://mbinary.coding.me +# Github: https://github.com/mbinary +# Created Time: 2018-11-11 19:40 +# Description: +######################################################################### +-} +{- +Given two different positions on a chess board, find the least number of moves it would take a knight to get from one to the other. The positions will be passed as two arguments in algebraic notation. For example, knight("a3", "b5") should return 1. + +The knight is not allowed to move off the board. The board is 8x8. +-} + +module ShortestKnightPath.Kata (knight) where +import Data.Char +import Data.List +knight :: String -> String -> Int +knight s1 s2 = let begin = axis s1 + end = axis s2 + notEnd = all (\tp->tp /=end) + in length . takeWhile notEnd .iterate gen $[begin] + +gen li = nub. flatten $map (filter (\(a,b) ->a>0 && b>0 &&a<9&&b<9 ) . change) li +change (a,b) = [(a-1,b-2),(a-1,b+2),(a+1,b-2),(a+1,b+2),(a+2,b-1),(a+2,b+1),(a-2,b+1),(a-2,b-1)] + +axis s = (ord (s!!0) -96, digitToInt (s!!1)::Int) + +flatten [] = [] +flatten (x:xs) = x ++ flatten xs + diff --git a/search/binary_search.hs b/search/binary_search.hs new file mode 100644 index 0000000..9429b99 --- /dev/null +++ b/search/binary_search.hs @@ -0,0 +1,7 @@ +search i li= binary 0 $length li -1 + where binary a b= let mid = div (a+b) 2 + p = li!!mid + in if a>=b then a + else if p==i then mid + else if p>i then binary a $mid-1 + else binary (mid+1) b diff --git a/search/schedule.py b/search/schedule.py new file mode 100644 index 0000000..27e7aef --- /dev/null +++ b/search/schedule.py @@ -0,0 +1,104 @@ +''' +回溯全空间搜索, 剪枝优化 + + +设有n个任务由k个可并行工作的机器来完成,完成任务i需要时间为 。试设计一个算法找出完成这n个任务的最佳调度,使完成全部任务的时间最早。 +''' +from time import time +from functools import total_ordering +@total_ordering +class record: + def __init__(self,nums=None): + if nums is None: + nums=[] + self.nums=nums + self.sum = sum(nums) + def append(self,x): + self.nums.append(x) + self.sum+=x + def pop(self): + x = self.nums.pop() + self.sum-=x + return x + def __repr__(self): + return repr(self.nums) + def __lt__(self,r): + return self.sumcost: + best= cost + rst = [st.tolist() for st in lsts] + else: + for cur in set(lsts): + if best>cur.sum+works[i]: + cur.append(works[i]) + backtrackSearch(i+1,lsts) + cur.pop() + def findInitial(i,lst): + nonlocal best + if i==n: + cost = max(lst) + if best>cost:best = cost + else: + mn = lst[0] + idx = 0 + visited=set() + for j,cur in enumerate(lst): + if cur not in visited: + visited.add(cur) + if mn>cur: + mn = cur + idx = j + lst[idx]+=works[i] + findInitial(i+1,lst) + lst[idx]-=works[i] + + + n = len(works) + print() + print('machine Num:',n) + print('works :',works) + rst = None + works.sort(reverse=True) # key step + best = sum(works[:n-k+1]) + t = time() + findInitial(0,[0]*k) # key step + t1 = time()-t + print('init solution: {} cost time {:.6f}s'.format(best,t1)) + t = time() + backtrackSearch(0,[record() for i in range(k)]) + t2 = time()-t + print('final solution: {} cost time {:.6f}s'.format(best,t2)) + print('schedule plan:',rst) + return best,rst + +if __name__=='__main__': + from random import randint + schedule([47,20,28,44,21,45,30,39,28,33],3) + schedule([98,84,50,23,32,99,22,76,72,61,81,39,76,54,37],5) + schedule([39,39,23,45,100,69,21,81,39,55,20,86,34,53,58,99,36,45,46],8) + +''' +machine Num: 19 +works : [39, 39, 23, 45, 100, 69, 21, 81, 39, 55, 20, 86, 34, 53, 58, 99, 36, 45, 46] + +works 经过逆序排序 +init solution: 135 cost time 0.000196s +final solution: 126 cost time 0.022922s +schedule plan: [[100, 21], [99, 23], [86, 39], [81, 45], [69, 53], [58, 45, 20], [55, 36, 34], [46, 39, 39]] + +works 没有经过排序 +init solution: 168 cost time 0.000179s +final solution: 126 cost time 10.646307s +schedule plan: [[39, 86], [39, 34, 53], [23, 99], [45, 39, 36], [100, 20], [69, 55], [21, 58, 46], [81, 45]] +''' diff --git a/string/KMP.py b/string/KMP.py new file mode 100644 index 0000000..432f0a1 --- /dev/null +++ b/string/KMP.py @@ -0,0 +1,58 @@ +#coding: utf-8 +''' mbinary +######################################################################### +# File : KMP.py +# Author: mbinary +# Mail: zhuheqin1@gmail.com +# Blog: https://mbinary.coding.me +# Github: https://github.com/mbinary +# Created Time: 2018-12-11 14:02 +# Description: +######################################################################### +''' + +def getPrefixFunc(s): + '''return the list of prefix function of s''' + length = 0 + i = 1 + n = len(s) + ret = [0] + while i1)$ +assume +$$ + t_{i-1} = T[i+m-1] + 10*T[i+m-2]+\ldots+10^{m-1}*T[i-1] +$$ +denote $ d' = d^{m-1}\ mod\ q$ +thus, +$$ +\begin{aligned} +t_i &= (t_{i-1} - d^{m-1}*T[i-1]) * d + T[i+m]\\ +&\equiv (t_{i-1} - d^{m-1}*T[i-1]) * d + T[i+m] (mod\ q)\\ +&\equiv (t_{i-1}- ( d^{m-1} mod \ q) *T[i-1]) * d + T[i+m] (mod\ q)\\ +&\equiv (t_{i-1}- d'*T[i-1]) * d + T[i+m] (mod\ q) +\end{aligned} +$$ + +So we can compare the modular value of each ti with p's. +Only if they are the same, then we compare the origin chracter, namely $T[i],T[i+1],\ldots,T[i+m-1]$ and the pattern. +Gernerally, this algorithm's time approximation is O(n+m), and the worst case is O((n-m+1)\*m) + +**Problem: this is assuming p and ts are small numbers. They may be too large to work with easily.** + +## FSM +A FSM can be represented as (Q,q0,A,S,C), where +- Q is the set of all states +- q0 is the start state +- $A\in Q$ is a set of accepting states. +- S is a finite input alphabet. +- C is the set of transition functions: namely $q_j = c(s,q_i)$. + +Given a pattern string S, we can build a FSM for string matching. +Assume S has m chars, and there should be m+1 states. One is for the begin state, and the others are for matching state of each position of S. + +Once we have built the FSM, we can run it on any input string. +## KMP +>Knuth-Morris-Pratt method + +The idea is inspired by FSM. We can avoid computing the transition functions. Instead, we compute a prefix functi`Next` on P in O(m) time, and Next has only m entries. +> Prefix funtion stores info about how the pattern matches against shifts of itself. + +- String w is a prefix of string x, if x=wy for some string y +- String w is a suffix of string x, if x=yw for some string y +- The k-character prefix of the pattern P [1..m] denoted by Pk. +- Given that pattern prefix P [1..q] matches text characters T [(s+1)..(s+q)], what is the least shift s'> s such that P [1..k] = T [(s'+1)..(s'+k)] where s'+k=s+q? +- At the new shift s', no need to compare the first k characters of P with corresponding characters of T. +Method: For prefix pi, find the longest proper prefix of pi that is also a suffix of pi. +next[q] = max{k|k\#~-1&RcU7Iz5}Ah=7hLW^4oQrumGyGxNmfIy%)rD%a- zrL?`_-FKfW=WMy>)BWf7BUDgn^Y001=93-D(Jpa8(d z#>T?t?DfUvOe zV|ED{aY1QbAz{J$M$qu^@CXSAsfdWE1exfV1pl9(KYajFT!0h04F(!B0G$*KgB0!0 z5a2OtoLFdoJ%IoDprK=6VqxRp;^7mZzR*kpKu5#CK*z+u!otKveLEQSIRKLsi;PJ? z4*Q|D6%MmIxnNjw0WOPtLqCPi>?x~|wMRG}KIJ1SYMLi(>>N)yg+)Zg#3dvZUMMOl zs{mDB>FVhl7#bPdfNbsT9l(yBUfw>we*OUwZz7}KzKf1YNqwINP0z^8Dl954DJ?6n zsBCP4HMg|3wRa2*4h@ftj*U;u!RHqi5lhR+&8_X7-7kAz_Ycm_zh7KlUEln;y~l+H z!1x<1)cfCn{a?69QMk}CF)=W4?s1`^`=SDa6cdX{0Gmus8^_B1A+ul@F1dViK|?XY%2_VEkLy?C;3U~&%zD)o^Mf-6| z{VF(15V!po`M5<0;bm_;ubXd-flIp5(2ty%q#FFHdyF2>B%80^UpRiJml-_FvJIDP zsc@=ZTQwUgBc^-6hCGstw44QK;>3B?DU{9f2Z%=h0l;{MwurW;IZc(5+sehKg|a~j zj+(HtA_FFh`0l!)htk@~A|rV$sz<}I)-U?T*#VxR;?&X}PQ@j&0_;^52)X*3x4`y! zTX_CFk8RHzI}?X5O365MjSpNuW`PkA$ix467=Fs~ny zd}L$6VZt^&7aXz&08E;T3T+N8-2iwddGF-tJ$is zR4rKB*+@Eeo+p?;tPEIfmH07tInxj}=b7^+l8UpPm)6sbtEs5aYH?_)99c5tbIe&i zhYgCWC+xFfCEN*1&MU8YJ(nAAZcpA35u^n_;q&_NwHQj(!-EeMa|ny#&NF8;0`yS%L!~Nb-+;${E3K&> zFJpER>`w3t1Jkw@n)2i>MT)G5^h0BiGY`_tqVL3WSk5Ax&@J zAcMpf&&~V9)SOc)QUpf1?>P+q8;Kuba2CJHLAB-&n(vJqeSL+5dDk6u-FaU$q|svU zX+7BP4HvI_;j6L@X0?5xr6xoN!0~YSn&I8xd;Ik40;>n!BNiF=DVvre>-Nw7F8rhk zw&n?LZ|lw&a4PnVp}hw)i=k|VF&u#MTyG^#xv>hz_$BLeEOBQy&Dy78wr;dewcu8` zsRGy^Yp7+W(JMyJC%T&o<2;ubXRhWi3TFDxS#O~c6rI>vgUTuEI=KEZkXb8M`VSss zZvF>>i2MZ+%Rrb$M`Y*ZX;l=IpCF&HF#^saZI5$aA$y>DTj%-YW{Rh zl=jxF^j3%cCU2jLx0@wqkTNJ!94WCYgI)uJ5-Mu#LdDp=3AauN8$kk9`wYy8UYOU)Mz|A1p7Rrz2a1pi~XIIEc7S1w%xSl6%fG9CeM1NHXYFgG3DE2n@oHe!N zc!PZ%Izrx4&Wh5KRJSE)r+ydU36-b7Rf3P#_K)k@>2W_F(+c>h6TlU|HXK%t`~Yns ztH{xkQfza<2Lh6_HpS+_*aI4~XyA0_-n}N=6o2sO`7ZX|+4D7Dshp9c>Y^&RQA?XB!(QfE~|Kk&t`F z_i~Ba9QZcpj+(b?mPYN2H<99>kdstaZZY`KkGphGTFJ}WMXixXxNf@|>ovrR?mT91 zjSrD#ms7*dC70bpS5qy11!-o5(sBGaoPILFoN)6OF-DD*MXcEXEdSPUUQ;Qh3`ZVq`eq#07A1$@ zZUV)3^z-hmDg9Gl?|ie2+3BR6lQ{7;b97Ao9q55(Il;b(M@oYPzF{Q0Z~bG z&0Axq4%@p{Qlqtg&nHbsIZSD$Lu+wpKQwV7_Vs_?+mp>kV66Q@wPlpU2@MfXp}>HD zz1IHE%wM4C=dA3juIUwZ&(nN|4pRh+LEhx5`{-+fN0-NZ+9`fVbu}lT*5FibvZ{;^ zxl|ba8>hjXe|hI*5Y_prQ-GJ2TA3Q86r2cVg=$Qt7|Vp~*}kH^OL%Gj#DDxtH#)em z_1#C|XgZs!P5_Qe=?8t%zQvB&)g$Q+|!K9})=;wU(otGI`hx7nFe3WdLI1m+l5& zniiV150%gD;olfL-v}r?>8fImqF0h*5plZjamdf{3qtFRwp1uM8BhL)TO+~l(WmioEtlJzrPrawLJJQ4|1*w;;;60+7oU6R2pLNcv zGnJ-C&AI{Lia$;J1#Jqfa3Zl-R+kxzHeX!aXZtDbNNb;=mE5)4lpqdx*vekm?c~*v zQwtAnlec|EoywK)S_|*l%U6zrim^nl>I;w_c$vAJKlWA+1eKc|Z)zPSN5s9@qEDNT zd~bhuy1V*=%U8y%Y`YpqQ)l0%4f6BmKjq2wO?ZUf!6WaKA$%C^d>lVDq4ZKKilDop zZ=8{MXfA-!_^U;tb~Dt-Izi8$iOE*cQk4u15O?Y(x7SeCyrWX`fwNK&*atAk^TX7n ztlCmxZ|n}&Y<_VL^qQsgC&X*Z<5>dbeMkUUN?CeK?pQ-GwBAiGZmEGuCL5>;{dL5! z=vpKIthf4E^SRtWRVQOdO^OM&nzQ0gyd}m z(U_On2U(A1@VJnfW$xz`Zx>X3Ip7ym0B~iTHpb?NG`|JRREi0&=ESr4sAq^mE1M4ebW8j?C+YtGs}ZTZ&Xosm}OVv;v#&w#K%ZzFx8R| zv>6u|^FLoX4ruItLsbX5`6pV+?pb3Aw{u`h@!EXf;GcRR%$C9C(`U=lj};c1i~a!g z^W;=)%I`fvg2{-MH8*3cxG?a#I-!Q9p42U(v_F6=v1O2%&aO*I;>$G9 zd7LMvpqUUUrzlBiKIdIg97ml_8RQ{OQA1j>x$&VWkFN`x;m~=1GyuoTLl^>=w$+fS zMAas#3sqCn&q{Dbq*y59gR$#7hF9ThKdG^(3dJ_g%5mFPd{r659Qx0|8`5U6LBrwdO!0=2DOO7sjyjv*gve zefMtqcdsXNYn1PMoIG;=R(um-8}cju%IJ}9yGex&{E3lt5nVrF`pY5J_&1Ck_`Mw4 z_+0Z9D=&sFd<0>!AIXu_~!FXr73k7Y76s4T-o`$deTbIp58%kPajc_c;Llo z?fN%ZXa>7l&IZ^6v?kl8RNtz_9_GFLL=nLO^j0SY#r+vT2D?w5q(wV56p%{ zuRAVA_1N7LYuv|t)bvxl>9n&;6LWk#-XADCTOV>;`&$l$VsuE*i09Vu^m%5i7ST$^V zm??1p`kO-EIG4_l48*WCG}Rqa{q@Q?r!CF0D~vrJ)Mr^;5Gp=4O@2a70~g zmSS-z1U0QofrRca&IN)vxZRJ%iZ5`Q_0-sozr1w07Aos(Ze4V~Xnjb1vIR9h0FL+ZQv``vH>z12QC!fm#auRZ4- zONl7y%?G0d-39M!F-X3tox8k9tY<}f4IC+#sySo+++wm#QL>SW>tU4^HKG1d1z)XK z;_S@82+Ib+QySa;TP+C0hm+9aVBXF?v2{DNeue_@iZx2S(4VS@oKpO%R=Hmk>XYb_ zShyxvThnf)ae?dt3b)h7N(@XJHR?s(5uOXp*m@BdzpTW2LIsdIdnLn+q%ptDR0FoP zEP4gfSE`%?fS!;f;}{22w( zyeIhz^Mgi)*}IEzNN8Tr3+_ghNqhJ7p|?>VZJvn)#Iw~{T{@u3%;yw%txDEF7Y#?p zsWXDr(amo4qhsTP9pmxc=Lr)aNEtqUlAP?&V^z%;!nH?#O$zNr-eWIynrq$mhtiZu zusLo^O3os7>b{w6)4ah$Jm=rVd@M|+&G=~Q+ppyHvnC#{zBFde93P0WLH`a##R>LUKBD^rP-o?^_#IL zRSnKtE&K=2q?8(+?|_M5`rwIWNzk(Dk^~Y}o?P0_Kf@HeW}u%hm|OqY8wjTS95%wr z>GORh_mWT1EvlvWvcdK5f@)vK9V}AVBcfI4gp-Uu&bEaG>iX{8(DClkukuL;jZ)F9 zF$HGaiB)vxRGFE^8cCOvF29V2ZOZB3vM)vKzktzs+s>e*p+|3hf_O1o=dG43)DI?5 zXC6Vbt`v07Cj2cjekWTG{b;g~$xS&o;$&j?POb!4HgH0twew;pX$grZos9vf_~dth z44gr8T3q}zE`7c!IhgR%pIBQTdNc#Lp2%js5sQ=lV}m z%U;-4P*eBI`LEfiioF2vp{m8$ynNGQyjDGK*#Z?ZUJ;fj;#8M?noN^-pdk&pW_c-H+DoMhjwecHoo9=z5SVfM{@87P+RvK z4JZ;UsIGkhn}^V9$NP5Y=e(@!`j6u|1bfl4#gYXrLHxsGj*d6iPL*n`CKMpvzVh+Z zNwg~)2P&K|qd3(ipIkjE9rej9Wa4~VX8a=fTpBBql{@xW%I^*q+^@1+8ohr$tz}HJ zZ0&AK4&~=&G6Ug{k-M^<(6)Y0T~Cq1&j5~_;7r@}W72~#at>;_~g4VV45H+DBPAuQcxdsc@M1@%`O>7#W$M4(?`fO+C3avC2619EK zc1wkPrk2#`wXd|}6UD6^oanXt9sz8oPxTQOm-2*nI~h=yIo!T+1v}qbEZ^m5I3YT} zlSJvvgaV{mNuKY=`5v3)kU(leYDg+3)C1QSQKy0EF&ve`_#B0y|7Cz0{k6I>riXbF zOib%vW^kyR!@- ze2_Be!)TqSdYQzU85pAqi!}x;o1euD-s(5C^@f8IxNur6zW~aA7Kz7OW8j@<+viG* zQXfA4tFdhvp;y#mv`s}6O5~Lu@&*=svg?Z_PLNlc-EGjd#apC*;g-r)rcr9P2in+| zYSrJmd~8C^YBmUpalb(IMe9Z9lV)7D7cmnmbW+br=^E*j8qm&z%BaULM_)I3^SN?z zd=wvVf4-pb5E`)XUT{$Ov4ew-5oFHt+k=e{*t)mp^gg5E=>7vA8F81WBDh@iEptx{Dd(!vlCbx$RRqPuD*H0BW@*l8@oYM=}4O zqbW$PD#XgqS9_r=@!65jw`Xa+F29Pzryt0+)o>1-LGwH-Z}^PiM}^;AYMpmuIIgi) zhgC4x!)|5~x92pgpXwL5MP8&;^VQULr3-xAdD-aiB3Wg5s$5XzJ^9r~%zj9W=1qs@ z6$sCkB1LJAMYx1IVY5Do1S4{qHZ@7=;3h)zgZ}ABNtwyi&WtyNRbVV6GE)WTvf$W@ z!^!Cnz=Uic*^^nV$l7671P-KwG^`=LlehxNiZ_1hHOg1mF`|vLH(P(z^c1e+8GV}o z9mjP0kuv@e$hLY{^pWSB@C=HRdY({v(nK+I%X1-`Jp5#^?~q0nO`nwkSDN&4dokLf zHl~aL%+bx~ntmt111++tP?|GbGx^M%Q~gP`7VUzSrd~x0i0X%}{Ts27Bgp|W^7DZi zW-b+_Xoug+%vWr3q(McrQ36Ys)6W+cw%gN{yA!5Z0@EWGpS(w}9&*E#wmBSD7TWE7 zWvVFts(c&AyKG1HqfY&N^4h=^a$jpRj;tzD_D1@ZNU*|BF6GYN^RD%#p*iQztv=Z9 z*>w8P-g0|$hy)&Xem#*_cg1Hh?Rs)va*i8|k25Kduy#lEn9OLV+|Nk|){;1x>*V|5 zWmsEs-}qL}I?ru(^7|5OWE0|?XZe8A_uv@pjrrRIk~)zjJ8~iZ)p5!aOzHbC@*lQhlGb9R(m(z8vODOIVCJUV@qSJ^=fVlfqfGQw@i95jjhUE;v*E6 zU2d&JOh>a&>#7VFsbM}*zYg{>_9y|E34fhUCG3BsnBU9{=kmlhYwfBVux;DBHQNX{9n(39NvEbV`?1Wfs9m- z>;QDA;laN;nYXD~;5p@;rWqahwrZ=>mlnv3W?aL^K+$u0dWMGn?i3G-cBeOllH(Xo z5~3qkcY`Fh)zpo8#(kNGUn6`)zgA$zKJ@qKh$JJ?9dA&e_?!*ez(1&pnY$CJG3hSJ znT10K)?ejme=kf%8t}2kB_DGD{s0W|Afp9Tzd4T`{;k z5f*D=#<}ZtR=U)`qbTelH6?C}X{S@kvf&~4gS~uI1|te&S=0Qk&)FWkWsTU- ztwvn{gnKWoWXV=j5NWumi-~^LRBrT$)uT8;xf~^cAihl*S9L`7rHua8(_C6=h+XRO z_tmcHsfYfdDgsne#A96#hV~LR2L<`{kwccELJ=CqJXrG+@F30NgX_|{X@6t0DBbTo zCGob;pYzd5MkaeGbE@nF#K3bV_0Lt>CMvA*tV}f{@7QbhJ1%p}DpwVS25oq%=Yo3z z(=(R~UHS2_S#Yd=JPKRNK3maK+DnI?g&?7Wlnoe*B^i&OQ0jFL<7qGD~%X2JY8V>boaFKpyea2ro9vE zSYxQ?;l{(nfrMT}(&B9jSn_vH zHLPKIdWxILXimkPrManjUYs^*V>3#3VkhMkmp{SuEkrUkAO{5~yS3@^gCe(~%&Ku1|vsIlIsy2fz{d#;RQ96KLdHgcwv;Sgd!AePqqu@|$rW)i() zSP)GxcH&0ye4O>xx(OC?JjuVX3D19_s4_Qa@VeIc0gB0agsX+>bX$yC7PgHz>~UZd1fw+qd?C(*%9 zWuJqg=MMCYdB6SJF|qH6B*68kOQ(b3sN z>}%hY>3&c+Ev>3t;fBcMRHrge;OXcAsrzvr$AfM(2LqxKs~sO*_vplSIxv&gTwFWX z(5h&FGO#Uu!zFD>#m5)VG}2{TH`NVPTm0q%JB^s)OISJVbgM@PThoWh=W*MQ{r2K-<>Qab|Gs~?-$VTW8AdeC zLp7CPV=on&xdzPy~kh)}5V%Z8UfLB9D!0bI$6BNJu8f9%6dwy zV6M6Op=t=;B~(7q@vBKwSj{seqylz@Kr!Vw`CnZHVAsaa*&6rjOH;M0{E zFOGqtXV0TE+umq~bF5U&z*zYxg|c;mHq|yM6}PIHiN0`b0Tkf!`qVa*ND1%%MEyUb z{*oZG6tOrVVJ==%y>_4;r`!3k0b5y%e<#tb=lzGq-pln18UrzoTH5}slcM1KucDI8 z#pNAQ&^n^8oqXKY<(-q+=yAR-m_qj1Y0?IYn^ix&i6|I6jQyTKk@_sOSx;#M*ArHM zi+IF;FzcNjD)aU_e_B+ZGxx%}x|+orm!$xw+cTZ!cZCch(khrm zA6}I8gVN|rE_y04vjXhhuAKxUgOd2=uzbJ@{EQQ6fjE`w;DWZCdBG3#`(X>}yyxd8 zv`z)?nKXviJFvj#xG!D9sq#9Lgod9=d;3yGI#te;8Y27s`O*o~jU$xt0kk~A;mj=K zrhKM5pbGIacM+SG!glsEp8}a-7>!*#)@sWB;p<7)c_s1`(W=UN>yS4W(mOSRhojsO znrk(Ch^w)Rb(j+>(I8h8qN>-L?3F1a~o4~OeWZpZW1 zr58KZwql(4suVIQ1*dV~U zxUg?9AjI7so%or3h6^3X?}u^r>$3C1XWmQUTX?WG1A~}f!WS)Sji1I@7?VhO`{duS z=B2WL`-FM)`VPLLKw^OITwIL~6ngBjFCKi6Ezy{Gwd%fhj=O<14Rg-ao6iP)7mBVlW9c=}J*=UCo z)U7{^eZe0kS=&J!23Oi{+sxcHZrikH3GPeon)7#$?JJ}nx0tW6E`9|2K#xMrCeTUOikXxAi0az^N!}Ki>mmo_=S+b^i-dqK5UsqT0c$XSR5UJ^#XBUc7`A(x%K9cm>0aNrX+p5qfT(bmT?Iw-dBvs z?P2G4%+{Qsj{2oX--p<1G!4QMGs6XgRz3LlN+pIJW>tLcpSf<*N(JjkHFtD5`MI!m zgb!y*zbj4k`GNh&pFJ>Jh(wGIZRCjsQ8kTXZk|o0qo@UFCSCeLmEeK>al0}Ja1B+VYJi@%RZT?+`duSH#X?Og}W3PLiRcv zT12C5q$#TlTzKG8Hr5j=03pH({XvERo07uFeCLxmBIN=7fJpe5OYQOT9%;=&F=04Y z(%W6)G;VBhDI*VY35ue!eJyaSWZY?29OC*h_`L)sm%l@X>4OY8g!Gp>ZFwI8EOPDV z|1hjpzeanOAGobPXG2|m(54?a89@LlW+Z+jg^jA`UbZWud1iEg7244_Q=Ik^agjout7UIKMsjgk|7 z*tY_Tl1rlYz?CNBm%}8(W!!3_?Z0ZJOg~$$}<^n|t(q>ljjvr~O z`})#vWQl2gQhirmB3SGtm((9NjuR2{xiIzuBf|o-pE|+tGFY2WXTNr8$ssQ88&!Ni zwoC@&1N1nCjD0(8sy= zzVF?0*1f+^)_Q-u*RyJRb@!g?s_wE~RePS6o;CqoMOg(|01gfS;GRFg(<&ehpdurq zAS0ooprD|kp`v3FVqs!nV3Oez;1E)g(@;~9Q&Q40a4^xnV56s`WEObE#>vIY%S*!~ zBqqo$%E80S{TC8AG&D3!3`|liEK+VdN;>ZU?d_=>z(WP9;KvZ)UI6fTa0qyCPyGPR zGo46q|F!}DvBAM3AR-~7prWB;JP&BV1>oTj5a1CJkdP1&pGW&YUk4EJknriaq>u^J z%urr96LNn@%txhvQ`1AFK6A>zW9|}+hE7aEN=DB3l8N~h3ojqPfS{1D^jjHOIe7&| z4NWa=9bG+r3rj0&8(WZ_tDC!rrP{UwF@a!6PCfAfo)m7aY9jGa%q0BGGXn<4dWbm^l-?;QoM0_$D#GrU#9l zNBxw@++_xxn1OeT@$4_w{*AN$HO7Mfk2w1ujQu}+tpJz^aL+Ff0S}M>u5X#Y1fl=` z3~&?R7&fsB7kRd-$ydCRH9KpfR&>eN(C9#@1N|~lf1;05B#PwD5HwTA`knyu1_Hi~ zFHeBA6rwVHIn8AZkrWd~PR_z8*rzBp+BFYMuAN@-CPS6D>4h@4SHcQspH({WM;_5; zBS;KI2evco$lK-_S|}Vkwfre0HXI}*kz&%^-cYByD8SiWxJhQn%yb6T7{pFG+`viV z$BZp!WyStTx|-pTh)LcR>wdt;++U|BEDa%S{}==H1pHM3?SP~09I|NCA_aa*{y`L! z>3M}xJp~U~q_@K&3-@%SX7er7)ffc!x;6f=b5Nh!6QasWru_tH^XHkP{YK2~ZH2hX z&HnVma~Eq@dvn!TB`Sasba>bn;&3_jBLg^Gu}$n1ji{8OQv&3vlx?OezZfpb*+;q( zt;K{{W^;a~G0EI#jhUxQHac?*LCz!R5zpjp;wpxVC8kKm$8Q$jUB16oq5Ul^jY3Hr&t$>dwoi7%URCtcS^ zspj{ZK6+1>f>O1s&!kPX8zVyN8m*8}5Kl&mzb#Aop9PO_5ctS_QJ|HcP89x-_zfD6 zr;1_*_P6oJOPxK$h?w7OS4ROJWatn1xd`!%SM!rk98zR|m546VAad#6%fXJTEOgD% zWaDh!k|JFO!4BAxR^I|>N$>4Me)0aB^ol|8%ZWhtj89@ zW$p_vM4jIq>B{xcm69%06Hf|#{K>u5Ln>6Q)W}$6#<2TIJ&*-Rt@~{xbt%TqQ79t| zVtDsjB~@1K3CM$mntLfd$}Dvqbqqbm`2ed>V-q6dk!>R-nK?JdhLMs=W!`u28L)xVKl z=u47Nw;U*P9A_*UR`tuS#r(-1T=V%+Y zJo|`ZfdAT~6YVSni)H8C-1oRwrct054Dz}t-LXq>-C{n;lGGU)p`X$qwrL*6F1bEL z2_!UHlXh~=Vcfk61?tf)O`N3EB`YmBUay2W%=X4AG2jHJW#=2; z8ww&=0Aqz6s=N{TE6XOP@7C-y;eO=6NnvMw)Z($ak!0$w%_InT=kKGLFoiX)#D-s3 z)5LQ2)imIv4l5;>@y*rV;0fwPRcw|c2yJk!M&$GBCPnvR-d_>PAQ)G<-c(6+`i@F# zQuqqC2r=0c&}obCrFFj2-U$EXYGpn5B+^Qg{rAXT@x}JJ-!DGVN}GVKCh8WbT#t6+ z?O?KNg`M`yMCrQGWE!GF{e5ie zXB>Hb4JrhpycxW>x^76>TmY9cB#-xqH7>#Skf$& zJ~5{rZguUj)&0{K)`~k_28T$t(ZWZ>d5uGBG3~)gLByJM)}|~*RTA5&^~2>W=zD(& zof&s3MYF9tBagw8t&?|Ow3I$#if?I?nl^X$97LHL>zQ~B^(>ORczeWy-D}0#Jj|Rl z=0NjeS6EyJ=u=q&xe_ZWYrA8w}Iy)7|iD4LCH6&CzLXK9Zs`{ykgPMMwy7uj6G951+`o_KKDP7UOH& zm{f2gHO?HU62p=7h(!PURU%2);<1MU#VFS4ASHgYh*(7}lu7ytNNU*by1lG>0;ctY z_={l;6k^zWj~DW~MIU3ovae)sCh)Wep%`C&og1Q_VTg#4bsC4;sdC9Bl-SKW{B|<* z1QZbGo$*!bbm6wMBW~8qaOkCp^5as{s7X@U`-Fa)X-_mZXqCHOwo)fKbRAG|ba(Wm zNr#APymW3Az~s3k8ZbU+1f9t#htj@{f{4qd@{#JW^f}1+X;jkO2@Hv|D9Hy8QN{37 zGIL@aJ2)RYF(Z8Se>2M3P@i<(rQn*zLO6YGLx zQNF9*bSRgESMgD>6N?G1uCLas^RSEt4Hul0ib?e#g`4yS2}eHxq_(XNZOF;){*ILA z6+ArSxyQC)cQcqTZJ#a(;D);Id zR>3zh){pbwlYD8yFXRUbllIm>=j;w7Yn#>tUvGDoI}nWPR?@P>j_xK;G9%Ol?_rdF zY`l=;wJ8|v3EyK8m}e`<&hs5}PnWVFOOb{QXGXJsD&vfZD#-m= zVbeE(1)=2N)oB1k@@J~v?>Tq&)!Yry@yuxAqwFElduD_gJlI_-D}Li;i7`T#Tc4fcCdDkAnYveYv&7P%o|9+%2n|`P7_ue z0_3O;3J&PK)5v}aes~4p=TK!IP7Z8ZCxyBM{$Wqey@CsTpk}RUc>?0~y4D=OOA@(F zyfXn2FJ25bb~;TCrWn4O1l9y4LRF|9vg$;+*1OM3zR4uucd4zk*CbEe(B$Q_A*eW@ zp`cK^yNR>Tv28Bivl0>?;E#Ds538Ei+B1Q~24w&bkHyBc%+4`)%)9p#qY3=t&4tbL zY*mbLteMSpNh-}r@`O#(1QRZunjV2fRc4K`ba9XuvWcpuTzUZF12>qyPY0)LvXCXy z{1dT^Uc^Z_V>J&xiupf)C9Dpud5$4QU&%d5!jgRVO!zAuX{IVgblQ-RX0GWYLT{z+(Y^Gub4dmFpncrK_ePJSD>Z}zjHKP5ET zhqb0$<$4MwPkj9}h7IdplNGvzY95?L_M`MQeKF^ze_RuZxExAXyfuwMU<#|ACI~Vx zpwCsg`~=q#xaF&KtaVA$+R>1s`;mu2?~Ql4u;q>rnP@PUo)P|x`~GEu)t#nC*tpes zr=IEBk0ciqA*kbjW!jF(t5T7p_}1CMbAhg@I431mI4pxxABkInNL8R+TVUo z)$q3ny#I07@s{Y4=AKI637{;`fP{9eCr#LF8A+LPY{FI4o|A^{j9(%Li6Pgq{T5MT z_f&_y;C?7k)lD@WNB=-d=fpLH+%omA-&JGfO+8rG{V3M#U9MdY(T5(#G`l2Olxn{5 ze@Q5HN&c#xtDKW%&dLcROB(-FqYO&vF0Nb+^4E5iEd}U3NhJt|zh?ApW3Zh1&@Ywl z!;qjBC3TRd#4^?s@TRli2?!~E0(L$H{^zHmirjOW5>vO@FfnjEPCQOdp;x?)pE$ik zX7914Y^+Sa!1sqQ!SYTsb$dPRSDLNR!BNIu;NP|6Z;vbwJ6>b@xIw6(nOg2)#P8(xG)rYT-edv*3i*8 zx7$JoMJmkK)*ckVm4f>U4EOM{<&e)=&Y9M}V%DV?k5XgxF8woNy8A;kqx)~P75rz( z3=%l@6T?K7&AC%;Pu$^hY;G2Pbrh5@DZXj5kw;C~==h4`bpu9)ZIoF@k*#Qw-OWU;xAzCKtvBFD6_6Y%kLOMx`(qi+oYJbN)3gw|CPyn)R9s zH?rXGQb@fmZ>hULZNwD&&nagH4Z)R@zQnl?e-{G9UNcN-gX?JaYf5%F7&&Jzu^ug+FhnC2Tcg(XLr;20_uJeQ}9vm3*|`X&W~K!At51S z(cE=I5EI#}-JZj&VW`;hmjh$<36Eh*{Q52pf?%B{ta>@BCR(_}^y%4Ky7ycNLhsa8s2ye{!$JewQdfqYrJv59JiKzJPl}hV9oun zwCLacWsooi7CbBKRHK5gzpoSS7qR(e)Dki{O{5 z_DFCnOmd2OLv8+*5*$G4eZgV|sta@*4vrtqRVU8xqZ^!uh=z>3thbLjj?GSYmNOe5 zza9!QH9ZcuX_wZ&onZSP{`gynMe`3)f`c7gflCe=8|4R*HV7jXP6**1SSFl~i|o~> zwlXi(=CeUTG=K0vfiPvCO$E-s`=>nY#Cj3AGd7HrH8N9}2n1s7dEh zB`(STLQ5sG532>Af!~f?l~%1R%l=BU;ItTm%4oKn4%DY58|fQ=1DzXkf(D-Lxa=iM zcaVw;zyz!LM^y7S8Og(L9i3#w&}#ZPtmfiMpJuMYkmSv0-V6sp>jb~Pk>i7NA_?fL z{L0`Hzu~I=4;O{d_@((D#^8|GOSv!kPla{nV9=_GMS}FQ8rpy3q!w4#|Pos+~pl4AL+GoG^_O zEQg+=^^$&r39T;!SsC_DY? z$A`q5+`%9Ut1|G)_pH{=Tn1cytIi2W2H%)bk<{ZX=csQp4T25X)Qf7Nibefqy*UM9 z6;U;(<+hDafN5s$*F!VX2xvhfiA#>`>2ngP5!^IeC6k)w=)i8)fEwhD>)(GxJrCzn zBg*&$y!dHyxv}#E$caH;jiMjh)&lo` z0%`SUq7*F!s#*nTdCLP>xy{H^Wzn9FU;;wX!;5s478@Z-Fes(!orifwXiR=qoM>il z#C9oPBHP5Kr?sQH73SmIOw-$jz6bgF>oT5T8B79Ww{SGWj7H>^ucB9YiNLj*0I0>4 zRJqLqYBGfXE$FbCd2GBqBQA^!+}0~rWM6oWstx$L`|mg;2i&#BK*Ih%!W=K%;|7)5 zM2-gAW4d*GL_V~0nhOpO(_ZJHck;W<(9XSf+t+5T3nWymVK(8?-THt~O}S7pNZu8< zIam0ns{Y8??O?gk{LPPjxckGQE79A|CP=wmV#IdcNPWY4bC|^7k7ECD#VT$r)@^cQ zh0t)#23!;|(Rqf@d#RlRNrD^Lt*;S2g@F#&nw@~tQel*n#<)EXj#GXts3rXE>~`?% z8et6TcI11Wo5wfq55VV`U0=U#%zvvQo%bsTW6oNp zOC1CW5W-W%2n;%D5-- ziapl^*=TE=R2~*ZTW;-YIy{Mes3rmYYGrB;wrW*eRgAc9lQ_H>p3x7I6p9jbiQ~M%9v*f zo-+k@GmUq^N9(3z8-ogY2a=O9^5$E-jPA4ck2ls3afzJ6CxG}Xlv}q+Bi=08piz5! zc5q{fGQwPUXp>8lVCG-w@qg$zTIPQfiSKkz{)5}o?;TX#P3{>pDiCM%b^^&Qwh*SDdQJtE^rNw07PD>e}Wudj` zr(%9}x~>OmbXx05x#|%at>HRISrLA4mZY*ud4T3y5q&Q_U~jKzVbJIy60ZF^`c*>O zFnK?NI&`ZIi{)0YIC_#1Pp`>PewhJcT#)2ETeqqE+c83zsxiByGaFR8Y8aV6j7As3 z%4&}iMkcyx!wUz~J@QDqO)at@bx%`Cr2pIu;E$48P%b`J;W% zv6|D@;hez{Z&iLXHTdEdPt<%5pm#lwU#k|pysn&31eug zCb>Az28jt#irVcGt?`6yMrm0&wst>YId#&;p7=>`W;%-NZhunQE9auUsym9eOdJ z#Wb;rfv&Ljz~aH$)jm@m2*F5mFEwpHJlc-vha4Uw#?(Zt+>oL!5AOyvw^_>;55|^w zLFyo1RTRAr*VsPs^b(z{rG0H38cayT%h%KeRN*W57vtjQt1!WDzUj_*meJ?w3#sui73!IU{o001}=@n>a-G*`Qy$$W0WPE&46@G~K z2T71yW=zF>N!e*Sbx)Qotb{xiSENv3a}80IB9zSWfyIp1i|f+7v{&{=wGunXRA?XU z8Vf2#$zwaytWa}q&GvBnKSpsn8AaR&odlA9ug}bWdoLF~RjZ@`b=$uEh^NS)>9q7l z{!qS31(U1oOd^WoJIX}d8AONv%Zpc}S7QV>( z=8zwk>m5Nyaao!kB*L*%5q#OAhw2Z(!?N$bYfNBwC*|!p06hniR5CqT2E&k#)}~#w z2?>RF+hw*Zt#+_;0t#tYgwIe~hnnW&6OoWLEfusw*TWUAmJAh6vO?XgAdEq51WGMZ z-IaUQ)?iw{whlg~G%A6L+K|hk^E_%(3=bc zKQj1h7@tvBI43jcOqBFiJppVxG1wswUtE6;01jBr^sQkVSF_tAC6GkkRrEF=E3&Tv z&E*@+%?)>i+W4y!6`4)T5LELLuO9(hqoGHwwW0x*9m4$awt;gTuM0lma;xEf|9p$R zV2vzbLQqYt>TFdq^xZPhKWR0;t(w$B*Z7z$N6b4L#qKS z0$LkGsuOz2D|#*wF6I%33>F!J>JQ1)3Y|N;N9(?%E^DqqSvp2It6Dc!FrVA?D&h=9 zD*R-@oM)MsAZrVMNz}(tT@jA~@~LmKIrdZ^tdT5-Z>@7~i}~RwYTlM0MTYVs2>sd1 z_j_EWaZAGVGixb{qekU4e$$%HfVV|_D?V=;P$`p5A}CqCV7^V_WJWNLstT|scHHes z4UD`QeeJ@t(Od*;2)vPc0-6{)&A1ZSYmp2UF*9`nDU*+*fM2ChYo)O}67HHJAW8LZq|4_1d>N5F>^^mDZ22fGCzM{K-t)nyJ-B?p> zshr=0c}v~k@KPs~44JT$)(3s7-DKOU`82f%=SsGkdcUSqNkEG-u7s$@YCF^xvhz3y zlhx93uJyz5+bIIvQerw;W*A^wGuvHJ-~+vB8NBs|1pJm;1}GKlY9$>HL_n|Sb$G)sZ6IUzp|}|CzM= z_mI6-`HmllO`HVo!BvU5fgMK;S2uc9nBcADG>Vms_aB(M#iq!EdjpA1&@hDm%j0ik zI)Po+9AWc@6GuKJF&)X+6VC~MR*X!if-!j*zquUQun4m2RNe%j+nz)v(SLQVb9xfD ziOLRDN}_YU7xBURwn3w~56(Fo2Qvj<%1)9CzANpkdrb@LOy^lU&~_cU-u*yTcc@7q zNnH9R3Sj;l6l9NkoVQ+TGCWr#8Hswm6dmzz(t59i`*Ub<`DW`#lV-U|FlCYD8r&H9 zs9wMA%DK?Gdz;vM&QQEAmln!&5@Q|2UkVL)O`x~z<*6{w$J$e)>V!d>;f+^gq^3KIf32V(FI+r#-cTY!bRKn zv-vuqr90xCXSZNgzgsF_A}Kc~k}hR1GXksrG#w23yCTxsYckt|BY}DH9FA0cZQ*W} z)n9hhK&d%Zm(d7X(p+HL2eR}EH@t$UBYR|uX-(ZF+vDe z=e$MWT(q=GrM_vnq-fdDwt`WDXc~7>@iw!X{|kq)PU4TxfAvr20% z9J|>&u3otqQmhs=gxgL{b|>aaoo4{!j_98ol$F$K z=1pL5rPL19o?_N@GUK3$Wg%v{F8+^sygOwu9bsgaItUhyiWKn;#T&NN;X_$Ag zYQQrzSReebva!YAIX4|APk&ZP^?I;Lr;};H?SL12hSk=acpT~Z@^XaRZw)gm#wH4A zdje36s+Ss8T|J$SD@zH6Zy0dfTv*jlMw0t9mLxG_8X@RDHOn8AI~S|$p`D{OhK6fM zYiFx+?RjDt9z=|tbyn^r6TTEpX@CVw?be*_2%-ls+_-HHPD@&EhDUapi4kLM?-ySV zA+h-;Lth;*Ih$IgZN8c;Ko#ox?KBhgau#F3prj_#fg_c2utks}-nesN1|AjB1xFFk z?>|kukFE(^^o@U@ylm3V!G8i0Jzn4ba(-@s$O+=#|C=^HYj^R;3u%HedZf2V>5-do z^DxHH%e^kh)tFG{kmVC_NHd0cj`Pd(6z*7(d@B&53=Z7{js&x8Q-_?Uq+VAQOg`#p8(O}^r)=3e$L}V7Iv0Be_x%J z&I>)aBO!eZulY^IqduWa;8Z*;h_}9qcu@hrUgmp_MY_$W6K|ZB$CP9SNS;@@@A=1r z+s{%wA97i&ND=QiyQfN_K^rTq!(n^4Gnm80QlF?FQz#C}1D8Kec|&%&x;;`nk`J=B zgXkWIBirBdr6y)*)!(ta6jU{Er77!;@6B&Iedy?bmf&R4Q_1S2>g>yIk|69OF-169 zHO?OYXh;M3u4=B8l#33RTp+@9;vCwgOZ22mZ5Qd-x9m4$>2v~Lw{gg+9Y~U|SZAVy z*RP4*eF>)h5Os_my^3(3QL=TX2fYM80l0?!Gknv!RRw0Fy&Y)R49QxR?3exqDq-hy z1Lk1RC+@Fx5y28HEzeb95J(&c#+0chAfURU%%fEv4j~RT4?8$&Xo20~;h4g|2>^B(lhVRv0^>}!>;MS)H)E7a#e>d9yZp2wB?od}v z){Q|;5Zh~eYB#QMZBwphgy%BUt@Z>VT((uAG{~KCH*S$639CvpTk^NslCQRL17e1C z7kC@h0`vke(|15EiGEV+>>G;E72oW%tn9PI4V+P@J#^&}Hn@|Zo~`T4`=F_X(A`>E z?Au~@GM)as8n>!Xwd9=q=y%31MEwNlFaOoG3%8_72rmy1%->EUD_?~WEp+A*YVgvS z(e`Ue4U#yj6oGosSGyBmdgOIaD98YWYKHwjoh92CtEKfiVH<-m?GL{gmG=}-Hc&e_ zamb#nh(GvhmF)OkENMDAiE#lvrMCSBMPsJYT>Tb(RmZ2qVM3g)x;%+^zBjiQ#;4!A zRvmuCYu3c4U-i?cklgiAQu=5egN+T?(M87C>y$mzMWmLqZ)x%<9|_^uPr}kNTJ4ZM z-_V^?b8?djI1Kt)US1Ma>0J@;D9*Whdl)_5&yC+8#T&Q79cwFJ=A{b8&ciFnT1eB5 z#jA=;BRfX@{LyvVm=A`KC3%~#M2KWX%{Qs1*05sY2I1Xk%SAjcSV11DEq_|pQxt|v zu+=<^%@9-wxuNppOJ`l!A@MrlFqKLTSsq$N%KZJSQaj;eXer-yl#)PVRf2vQUbs=A z`;Vx@(mc$gqn$#K62s3$o>0_$4J_8HZYplOxR5VOHhC&YV?WzFxRpezMT{QAvjVi* zHSy7aonl}0X=I8U^M&6SR21XR5HN$vT-x~(C(5B@nL8D{n6!3N7f(Bc5Z|B{E@Ak) z!chJnIlgKcz}5Jr4va`t%)ao?E=9@E7^URJO-_)G93_|#OpeI{K}ho&KwB z>2ILA{#^ObdIBydx>A(oB~7J(kRIe9(Z#KyuRfl~acbhvc<)NLfGp`zPpg6P^G9O4vY0J+a4uBN)-?O$Y|1Jdw^m3VbdDhz&v+ z0gXei{W?s0iKvQ_Ve(>RGZ3gKwR)Vb-Hg8?Z)$(;Dq|$+`DQTnKwu?fsSH$qXrU(5>*44C$G_1qrS`<5Kid#XoN|n!#W%v}9m?Fs`IjHb!K!!TqjA(Uz z$ihDD_1Jg~v&U^Cw=F{OX;#KZY7ztbihYmbHC|7TYwh?w*|n1mzpX{66CAX!_bSqC z%by>{+T%^BeHiIfQ7@n8BssD}ed+K+e$LSWfBWcteU9y ztBcO6?5)O1uh3%_t&Oig`fzZW@L2qR-~c7zDMFHy0Mbp@(QhN{Qww`mPN)p24%f z+1utZ^ERcg%lf+9(v|Ot zdy84?L$qV9kUv1AdmT~uJ z)NbUAus_akiU>sH`e-t&k>41+CQlb8_%Y7XKZ3-Sy3%<*S0A{@CX&1%5CY&K(8%kD z9oTNhT83PhpAe<-8wGDd&d3v-lkJBkAu*nx+^nTatZ(c1Bf;paxKbZw;Z4s2jI(xU z_+pC&N|u=#f(`7E&W$-oK1@SI&4UC>i`dVM2e7|1#qhRx{zQkTIvOOo*v)C+Pw@tq zc-;+Z7_ap~sM)@`P2xgucSNs->G>87mh@H-iD3zE=+#To8^xu!L&=h!$LW)FQS zKn|)9g&am3wS6=)LbwSpJGVLaDclu14LPda#lbxudZY_l=6vTvWYlu!n)Z1=qtX>Y z2;a~46szx+DQZo)zB{x!R^BDLhp1(~HjXakF2uGe-S-2Ew4tWw#0!7Ka#q=+S zKn3I>J$GCM4!4m!cs4%)GgAickz23-keELK&}*7op?w60f6UA=aVS>T*S(l@g8K!9UnjLmVJ}Shy?NU0mw~BzsD_dPFsp~ zTZ_0>LyO=Aewun6IC2-Xhd26#*LwFO*(Q3|wr_-kC=s-tK#nC= zd=o#0G_b-^Q9+b`OTGZaaC#CG(Vhxs4x+g^ep&QIUwRA6ct~dMIPXrma5T~2ZB0d+ ztmHm0vXcQUHPPzztEjLAvg|$5R0NtDDLto4isu~)!(+@aOK4#Amr6!&ekn!uPQboR zs!?7e-p{-gS`n$)7r|}_7#zhC9>iePL_tC?rq=p1cO z!Y*Iq9j-7Qe@sT5Xc!KCow&=vfEdzz5iA^cd3cF6QPl8LmbJpGB<4~$~3t(I1%Us^J&7ZSs)B9Ky#;u7RA~dq8mHxXT7U$imSk-)v`SLoOYfgc)EW#yp zd+OcSxldIou?=6v-r}*?oD)%37xh$vl65B)D}#vK4`Z;UahxQ1;dBN_@N~U=eyM+I zWHa{-RrRt)?M65nxj!?kTu}sxlbm)2D=3u}e<=BUE*1Hz^;^GW_ga8v1&^z+OJ<8yA`q7EBsWL5g3 z*(7?-mr^h4S9GWE6Ow`{4a#~*H_s*BX=bcvwf5H>SZhX2Jo;uy{9;fI*rX0`srn=n zON5K0MGANj0*C)y8Sg*Ni_Z0K@HfqhQ?DA^HK`kJjJb+Bzct}j^xM;-{7r?d0^iy} z@ov7b{c#ZnRar@wkD5ipNrun)cHGo*&zgSYyLm@^YFSo(&~-rK@;(`DaSv^eADDVS z6ki4L3V=vU&p+qsDdx4onf^tUuNzabNS}aI7wT~oS6C2L$xW^rEMS9oogJ;VC22ha z~8KV!D1I=ASyxMFA?fcAi%O0;jv2;R#ciTgN+o^!zh1 zcL*Ex^$!8RZ({3>)JK__yf9Bixqs=XI{ADEW=T0$@!XlhG-0eCLv3v4Fyi{2DiyR5 zmK$ip`whR+3U8G5aV(WTMG8*AA^oQ9$-X-hSFcMzB_+! zwx_E9DF3+q=v2`+_*zMyKmEH+ev(#%Z-&?uM7?fxJL)pzjg!S zC3$6{JZ2T^c1~D!$nw8!$8kj{qr*kfB!0yNFf2T`rd&PuiVCNc_n)m5S2NDbZ$ckx zkrDQvODha3S0!fPhx}4dBymoGTTIeP6650qj?_0IswW_t9Ead#sEzre4uNjf8e3J6 zl!vj3DgO`gO)Y;5J%6xd+pPUhj)8|=EC+RY#Y%(0+3|HwZWSWpv?8UFd)WxoU9gv^ z2+`N9I1ILlL4W5ENfLJnB9$V$4C6HZ9P5s*FgY}gT@*v-&+mSK{iePo0t5 z&CM-|=u%D|m>dBP;&w*rFpxOIO~7|Ek1$rHyji3zh1Hmx<)Sm0`U|hj-dY)gRvAxv z`Oy|ses}Rta17M63w#PE?~^USj2-P$7~U5t9#)&1fD_M=5mwz8W2K6yv1VmZyZ7W= zoUHF6TBV*XiD#MszV0DRIQ8OUrJ^h`T$Un0Bi9Eu=O^Y}3exrz9?UQ8E9GTolIP%O zHszZ3ynHT72jb>PJ8wm+IrDO^+szC}B(uuup*Kp=hD0Un&Q!V?Q6Jt8X|*>rhw*zY zpR7pgV#8@O2_2|<{gMYCh%$k8m0caF%Sb<;zweGgGndGABYq^9uCl@(D0xfifieJf_r7|AIZ2qR2q7pk&cBisD_~qmldXXW6+x?Pk#kXaj z>sW6?xFO?k@vSbMw_k_x`Vz|zyVzx>C9Q8HhKIzhZ`F0tzB*VKq6X|V;E zoj+^~N7)$^eT(xG@HYc{!D{537+bP+T`UAAy&qWS>-gi(2vj?p8(X|MD$V8JagnTz zzL%rI5!;8SQqer)4a8$-gW!*MU#Sl#v{w>!n+gbw^;|)>>FGi-d)_uv1ZM^LZZ|)e z%hjIkB~~gygzEY6yWcsdv3ySnqIKKUF#tJVwaG&f)!#Cxt}>EZ-9`Z!{|8MIoc9_~ z{_rA(gclv($44M`u``9PA9Bq`?pcL+JNOd#^S6bp+i3!1I`BdrZd9F67Wq5}RtQ`7 zGVLLH+E;@Iui2X(W)GI`aOLm8pM_rsTUz@|_rAv3Nt%^~q>osP^iYxm>oa`bRrgXa zKX`gIeaOF)$)1$rmdnC*?xDRdiiyYd>OB;Ce?&!Z$0m2u{!@UMzH?eu>_SUaXlua8 zn=kb1<79nf6F-bUx4-!9@#ut|8OmIPVGYrQ9xv@Au%I;%luMTKO_~S)$pz;Eq z-M-4I6~upE#gj;WaY!g|pnBl6E*H2Bv0Z7&-aUsT#1PBR#rP64J(qLBW>4mHYCYre z-zdrMA5F)hCx8j0QhtBKwWFr?v<_o1$W$I;G`A1#3PHFrk4G5bB`25TZp!GN5NS<$ zsV!s{axj~2mkYT`@%5(R($i(E;M_O*+(M&@dcTiQ{=X7@{51wjJ)~wrl-_{Rp}TZI z?K)tCse!uU^sy{46N-ic_~|8j3#=W zhh?yzCR^|O4F~+vn|*%xO2f!|j2l~?jPb*e-$I8;|I1%}`fjb3p)X-nFFp)uVsZ~1 z0(`+M#2>ZNdUMF}OccYoJWlID=wEvz1eikRhyt}|3QkYbw>0qBA%#XSB^_`EUTlsCruVqmlcXzih z{KVsWt}_t_f3h@)8g*YWWd+p3HT*Nj@IN_5!ZOgG`_1~c^*M9@ zIY%ZEd(eE+hZ!A>VY@DEaD!$IF|Ae-ZSW`>WM*=AFpmSP31Ds;D)tJH-v(v7ufC}% z16q*2!ZBd`xHxNHDtkUR2Ql5#Ez%h&)~zWMMijKLQiuplWBZpPK;`ssg*mOv5~PM- zq&8EqF8y{ywH4M*EQ?OBO`-+dT zT|52K+IE;#5;(J^2;t{Ue0Cy+6Y5&27&F53J`{gUzrJA+DK2H3=2!__0Wz^L!UzwR zp$I`9p~cI=q2f}%p*<8%3ukkmu+h$rs^I=2KF@;oCzr{}c&LSyAp!vl-Ik3Y6Z zzi<{_BtugV;wvB|%k)%EML&3KAOpF`D>c=pqwpFhyJm=%&3Q_3^OE#s$Y3YyA_TVs zmgsEd+~6~UlNhOSW|(;LB}hzB&mx#4LkEo%2fQfm2hxYrWng7Y_Asy zDjph^Pq_dA4efX6D9N0p&w?XYZRR^m6wR?wS>YrB$TM~w6M(S&8Ykr`R(mtl- zZhkv9@}S6^kkf2Ahcuw=gfZtjoI;)3|76D;r*^Ru9;CvR4VllSsqsT|8742IlnKg z&DP02ou6JHaQIyTbB12NT&{wdHsEEQIIW4YV-e_zvu&_7Cm-umZGMmZQudHoEwTi8 z5lyVw9Klci4dgS)V4U^5aAgWqO6Lm~W`z32)~1dorD(l^3=%S(*k?l-)q6(TXe9kU zi8ujFF}jNCq?a7KRW~^9KGi5xDTbnPe(#o`zqG!za@Yv*WGosFFpz%H$gRT!vRgR4 z4^cn!jHurIlL51=iI(Ut&yN!?iUr)Ii-;!e^!Ext+Za^h8!IGABh(Cf+?DWh%_CXo zEp*N?*HdItoNvpx?j~SR&p*x|4cM;y6x6Heb z@YRc{H;(<=SVDd>u3HZ_TJwA$YinOtHG$;<1$4HzN&G>`&fgfOQGCs|mFYFIq#$0M z-ekC=u>^^SMBNfa2J=Z1zgJV{MZ0P~Pi61$PjW7k^ok0W-n$ihiHLyyEp5 zTKQ24p>$Z}n$oR|!#(mj>4JHqND-vg5V>-?mwsPnh( zj=X`sH~j=l9})t&RJ~MGlQmC^(|uo!Cv_Lamr)Yjsv=Jis!1aKm!L_KXsB#I0(N2F z6YLM;y!nxXmWQ@qsq%9@@G%Va2{=UxyeXHcbAJMEN$H<}ZlWh(yYx9*)c-iK_yp{m z#y$A*_WwQ#_^uc?jE;al6(us$xa~33uuH%5H%+N3 zqwPLjt8I;OptZ>!eD)8lJwYawsd(-r-kLJ{u!;FT73b4k?Lk3KZWx$@| z8Y)c8lt&#RpfA^xE^+| z#XRT7SE0Is+Tv|N_h`^e{TTx_U)Bn}$0>jF@5j6Jtiye**oShz`Dp%z<)4%T`|J&? zA++;MwQ=+m&ppg~P57K>7BT)@@1Oto;bq8H8#^}$Ux9tP%xw~9TqUqd?q8A!fl+7Y zE7)u+2dbyaZ&1DKY2*}AP5dczJNrEI{@Wu``#T;{H$+zfXeLaslVGh5e+k*7LEgb5)b)Uz?zl&ZI1^!J|;{PUUK#A)Yuy8xq(&4p7 zSPzxHN?nCxE(j5M3Y{C-IisV(Hp%dt-k}Ep19|9|dfUmug2AP=7!(IMlp4Zse?#<7 znS~eR1>ddfFRH<;nyl;AAe&7nb}giA3O!6zg1^+gOx~tR%{j3DUt2a+B{T2Y4p!p^ z*-h*wXvqV^?+Y((jg^bM>^AS}n&(qaKaI^hqw=Y2;+&pu7Hdyyyi3~BtFIpUj{W{b z;SAs^-v?pocw>|ieLfjAVAdus$hIn7bldR}>!+(T+g_`x>m2R>2s~p#R#mhUHB!*h zU+%?-`2^BE+g&=C5qtH^?AUzYNyk2V_I_~IJgm$-;lMYAYdK~2G`96POrKSHsgLEe zw({dUJWch2L4*<>-z5|I4fp(4+>5DQAGCupWzM<_tE{xvCq{ldkP&CR*RyEbteugI zCj7lSe;>d){o<49M=xd@c`Vb= znRQR&=ZUg{$o;B;Kd*>Yv3&dRI^|Ad=}Mn-s)7I3mbC`{NdCGU%M?EAt<_s%+*@I`a5sSlY#} zt2^Q(x89g%E|F@+-DLlMRY_IYPLXVxsxMw$2BJA43_u}yjyU2-8B2{z+@6Ue}cimm-kt&R4r>Y0j@AQbj;(Du2t+IO(W5U zo<(0|!xj3Mn(+!pY~@q9_OQoVJYgFbqx#Cdje7{ck&4+b$ z=}CsbIU(clFR_*-y+Y^xgD2(-Ewo$0S08oGN^Qe+?Sj%BKBsm&&t^R#ci+?-wWx8B zE(uddF(196V%+YUM4%j{i5XduM+V@7((B7@mGA2$LAfajr2b1@AOKL7ul E0OQxkNdN!< literal 0 HcmV?d00001 diff --git a/string/sunday.py b/string/sunday.py index 600cdf0..6541140 100644 --- a/string/sunday.py +++ b/string/sunday.py @@ -28,30 +28,50 @@ def find(s,p): if s[ps] == p[pp]: ps,pp = ps+1,pp+1 else: - idx = ps-pp+np + idx = ps+ np-pp if idx >=ns:return -1 ch = s[idx] if ch in dic: ps += dic[ch]+1-pp else: - ps += np-pp + ps = idx+1 pp = 0 if pp==np:return ps-np - else: + else: return -1 -def test(): - s = [randint(78,88) for i in range(30)] - p = [randint(78,88) for i in range(3)] +def findAll(s,p): + ns = len(s) + np = len(p) + i = 0 + ret = [] + while s: + print(s,p) + tmp = find(s,p) + if tmp==-1: break + ret.append(i+tmp) + end = tmp+np + i +=end + s = s[end:] + return ret + + + +def randStr(n=3): + return [randint(ord('a'),ord('z')) for i in range(n)] + +def test(n): + s = randStr(n) + p = randStr(3) str_s = ''.join((chr(i) for i in s)) str_p = ''.join((chr(i) for i in p)) n1 = find(s,p) - n2 = str_s.find(str_p) + n2 = str_s.find(str_p) # 利用已有的 str find 算法检验 if n1!=n2: print(n1,n2,str_p,str_s) return False return True if __name__ =='__main__': from random import randint - n = 10000 - suc = sum(test() for i in range(n)) - print(f'test {n} times, success {suc} times') + n = 1000 + suc = sum(test(n) for i in range(n)) + print('test {n} times, success {suc} times'.format(n=n,suc=suc))