提出 #47768714
ソースコード 拡げる
#ABC329E Stamp
#Z-algorithm TとS[i:]のLCP判定は、T+'$'+S のようにSに出ない文字を挟んで実行するとよい
#Reference: https://tjkendev.github.io/procon-library/python/string/z-algorithm.html
def Z_algorithm(S): #SとS[i:]の最長共通接頭辞を求める
N=len(S); A=[0]*N; i,j=1,0; A[0]=L=N
while i<L:
while i+j<L and S[j]==S[i+j]: j+=1
if not j: i+=1; continue
A[i]=j; k=1
while L-i>k<j-A[k]: A[i+k]=A[k]; k+=1
i+=k; j-=k
return A
#入力受取
N,M=map(int,input().split()); S=input(); T=input()
#Tとの接頭辞長(Longest Common Prefix)をFrontに格納
Front=Z_algorithm(T+'#'+S)[M+1:]
#Tとの接尾辞長(Longest Common Suffix)をBackに格納。文字列反転してZ-Algoでよい。
Back=Z_algorithm(T[::-1]+'#'+S[::-1])[:M:-1]
#prefix combo/suffix comboで文字を消す 消せたらTrueにする
Left=[0]*N
#操作1: prefixを連続して取りのぞき、最後はT[0:M]を取り除くコンボが可能なのは?
#T: ABCD のとき、S: ABC AB ABCD は前からのスタンプ位置特定で消せる、みたいなイメージ
#尺取法で除去を行う。prefix comboの始点がLt prefix comboで到達可能な限界がRt
Rt=0; cnt=0
for Lt in range(N):
if Lt<cnt: continue #考慮済み
if Lt> Rt: cnt=Lt #prefix comboを追い越してしまった場合。cntをリセット
if Front[Lt]==0: continue #無理
Rt=max(Rt,Lt+Front[Lt]) #LCPはmaxを取ってよい。天才の発想
if Front[Lt]==M: #コンボが完成した場合
for cnt in range(cnt,Rt): Left[cnt]=1
#操作2: suffixを連続で取り除く
#T: ABCD のとき、S: ABCD CD BCD は後ろから消せるイメージ。
Lt=N-1; cnt=N-1
for Rt in range(N-1,-1,-1):
if cnt<Rt: continue
if Lt> Rt: cnt=Rt
if Back[Rt]==0: continue
Lt=min(Lt,Rt-Back[Rt])
if Back[Rt]==M:
for cnt in range(cnt,Lt,-1): Left[cnt]=1
#以上の操作で残った文字列を考える。
#T: ABCD のとき、ABCD BC ABCD のように、末尾と先頭が切り取られたパターンがあり得る。
#この文字列たちがすべてTの連続部分列であればよい。
#さて、部分列は互いに距離Mずつ(ABCDのぶん)離れているので高々N/M個しかない。
#それぞれでO(M)かけて「Tの連続部分列に合致するか?」の判定を行えばよい。
#・・・ただし、少なくとも両端は除去できることが前提(コーナーケース: ABCB,ABC)。
if Left[0]&Left[-1]==0: print('No'); exit()
U=[]; Lt=0; Left.append(-1)
for Rt in range(N):
if Left[Rt]==1: Lt=Rt+1; continue
if Left[Rt+1]!=0: U.append((S[Lt:Rt+1],Rt+1-Lt))
#各Uに対してZ-algorithmを行う
for word,dist in U:
result=Z_algorithm(word+'#'+T) #Z-algorithm O(M)
if max(result[dist+1:])<dist: print('No'); break #だめな場所があればNo
else: print('Yes') #全部部分列に合致した場合、Yesを出力
#ここからデバッグ用
def solve(N,M,S,T):
#Tとの接頭辞長(Longest Common Prefix)をFrontに格納
Front=Z_algorithm(T+'#'+S)[M+1:]
#Tとの接尾辞長(Longest Common Suffix)をBackに格納。文字列反転してZ-Algoでよい。
Back=Z_algorithm(T[::-1]+'#'+S[::-1])[:M:-1]
#prefix combo/suffix comboで文字を消す 消せたらTrueにする
Left=[0]*N
#操作1: prefixを連続して取りのぞき、最後はT[0:M]を取り除くコンボが可能なのは?
#T: ABCD のとき、S: ABC AB ABCD は前からのスタンプ位置特定で消せる、みたいなイメージ
#尺取法で除去を行う。prefix comboの始点がLt prefix comboで到達可能な限界がRt
Rt=0; cnt=0
for Lt in range(N):
if Lt<cnt: continue #考慮済み
if Lt> Rt: cnt=Lt #prefix comboを追い越してしまった場合。cntをリセット
if Front[Lt]==0: continue #無理
Rt=max(Rt,Lt+Front[Lt]) #LCPはmaxを取ってよい。天才の発想
if Front[Lt]==M: #コンボが完成した場合
for cnt in range(cnt,Rt): Left[cnt]=1
#操作2: suffixを連続で取り除く
#T: ABCD のとき、S: ABCD CD BCD は後ろから消せるイメージ。
Lt=N-1; cnt=N-1
for Rt in range(N-1,-1,-1):
if cnt<Rt: continue
if Lt> Rt: cnt=Rt
if Back[Rt]==0: continue
Lt=min(Lt,Rt-Back[Rt])
if Back[Rt]==M:
for cnt in range(cnt,Lt,-1): Left[cnt]=1
#以上の操作で残った文字列を考える。
#T: ABCD のとき、ABCD BC ABCD のように、末尾と先頭が切り取られたパターンがあり得る。
#この文字列たちがすべてTの連続部分列であればよい。
#さて、部分列は互いに距離Mずつ(ABCDのぶん)離れているので高々N/M個しかない。
#それぞれでO(M)かけて「Tの連続部分列に合致するか?」の判定を行えばよい。
#・・・ただし、少なくとも両端は除去できることが前提(コーナーケース: ABCB,ABC)。
if Left[0]&Left[-1]==0: return 0
U=[]; Lt=0; Left.append(-1)
for Rt in range(N):
if Left[Rt]==1: Lt=Rt+1; continue
if Left[Rt+1]!=0: U.append((S[Lt:Rt+1],Rt+1-Lt))
#各Uに対してZ-algorithmを行う
for word,dist in U:
result=Z_algorithm(word+'#'+T) #Z-algorithm O(M)
if max(result[dist+1:])<dist: return 0; break #だめな場所があればNo
else: return 1 #全部部分列に合致した場合、Yesを出力
def solve2(N,M,S,T):
S=S+'#'
#DP[i][j]: S[i]をT[j]で塗る状態にできるか?
DP=[[False]*M for _ in range(N)]
if S[0]==T[0]: DP[0][0]=True
#DPを回す DP[i][M-1]=True のときは、S[i+1]は(上書きされたスタンプが出てくる形で)
#T[0:j]のどれかを押せると考え遷移する
for i in range(N):
for j in range(M):
if DP[i][j]==False: continue
if j <M-1:
if S[i+1]==T[j+1]: DP[i+1][j+1]=True
if j==M-1: #隠し押ししていたスタンプの遷移
for k in range(M):
if S[i+1]==T[k]: DP[i+1][k]=True
#DP[i]のどれかひとつでもTrueなら、i+1を起点にスタンプが押せる
if any(DP[i]) and S[i+1]==T[0]: DP[i+1][0]=True
#Sの右端をスタンプの右端に合わせられる、すなわち S[-1]をT[-1]で押す ことができればYes
return DP[-1][-1]
#ランダムテスト用
do_rantes=0
from random import randint as ri
if do_rantes:
for _ in range(1000000):
N=ri(2,15); M=ri(2,min(N,5))
S=''.join([chr(ri(0,3)+ord('A')) for _ in range(N)])
T=''.join([chr(ri(0,3)+ord('A')) for _ in range(M)])
if solve(N,M,S,T)!=solve2(N,M,S,T): print(N,M,S,T)
提出情報
| 提出日時 | |
|---|---|
| 問題 | E - Stamp |
| ユーザ | navel_tos |
| 言語 | Python (PyPy 3.10-v7.3.12) |
| 得点 | 475 |
| コード長 | 7024 Byte |
| 結果 | AC |
| 実行時間 | 119 ms |
| メモリ | 94136 KiB |
ジャッジ結果
| セット名 | Sample | All | ||||
|---|---|---|---|---|---|---|
| 得点 / 配点 | 0 / 0 | 475 / 475 | ||||
| 結果 |
|
|
| セット名 | テストケース |
|---|---|
| Sample | 00_sample_00.txt, 00_sample_01.txt, 00_sample_02.txt |
| All | 00_sample_00.txt, 00_sample_01.txt, 00_sample_02.txt, 01_random_00.txt, 01_random_01.txt, 01_random_02.txt, 02_random2y_00.txt, 02_random2y_01.txt, 02_random2y_02.txt, 02_random2y_03.txt, 02_random2y_04.txt, 02_random2y_05.txt, 02_random2y_06.txt, 02_random2y_07.txt, 02_random2y_08.txt, 02_random2y_09.txt, 02_random2y_10.txt, 02_random2y_11.txt, 02_random2y_12.txt, 02_random2y_13.txt, 02_random2y_14.txt, 02_random2y_15.txt, 03_random2n_00.txt, 03_random2n_01.txt, 03_random2n_02.txt, 03_random2n_03.txt, 03_random2n_04.txt, 03_random2n_05.txt, 03_random2n_06.txt, 03_random2n_07.txt, 03_random2n_08.txt, 03_random2n_09.txt, 03_random2n_10.txt, 03_random2n_11.txt, 03_random2n_12.txt, 03_random2n_13.txt, 03_random2n_14.txt, 03_random2n_15.txt, 04_killer_00.txt, 04_killer_01.txt, 04_killer_02.txt, 04_killer_03.txt, 05_handmade_00.txt, 05_handmade_01.txt, 05_handmade_02.txt, 05_handmade_03.txt, 05_handmade_04.txt, 05_handmade_05.txt, 05_handmade_06.txt, 05_handmade_07.txt, 05_handmade_08.txt |
| ケース名 | 結果 | 実行時間 | メモリ |
|---|---|---|---|
| 00_sample_00.txt | AC | 69 ms | 77412 KiB |
| 00_sample_01.txt | AC | 55 ms | 76788 KiB |
| 00_sample_02.txt | AC | 70 ms | 77400 KiB |
| 01_random_00.txt | AC | 66 ms | 83532 KiB |
| 01_random_01.txt | AC | 70 ms | 85780 KiB |
| 01_random_02.txt | AC | 69 ms | 85640 KiB |
| 02_random2y_00.txt | AC | 103 ms | 88900 KiB |
| 02_random2y_01.txt | AC | 109 ms | 92196 KiB |
| 02_random2y_02.txt | AC | 95 ms | 84692 KiB |
| 02_random2y_03.txt | AC | 109 ms | 92340 KiB |
| 02_random2y_04.txt | AC | 116 ms | 92560 KiB |
| 02_random2y_05.txt | AC | 119 ms | 92768 KiB |
| 02_random2y_06.txt | AC | 107 ms | 90152 KiB |
| 02_random2y_07.txt | AC | 109 ms | 92308 KiB |
| 02_random2y_08.txt | AC | 118 ms | 91980 KiB |
| 02_random2y_09.txt | AC | 118 ms | 93768 KiB |
| 02_random2y_10.txt | AC | 96 ms | 83984 KiB |
| 02_random2y_11.txt | AC | 119 ms | 93256 KiB |
| 02_random2y_12.txt | AC | 108 ms | 87780 KiB |
| 02_random2y_13.txt | AC | 118 ms | 93572 KiB |
| 02_random2y_14.txt | AC | 104 ms | 87348 KiB |
| 02_random2y_15.txt | AC | 109 ms | 92600 KiB |
| 03_random2n_00.txt | AC | 88 ms | 87036 KiB |
| 03_random2n_01.txt | AC | 91 ms | 90004 KiB |
| 03_random2n_02.txt | AC | 79 ms | 83048 KiB |
| 03_random2n_03.txt | AC | 94 ms | 90408 KiB |
| 03_random2n_04.txt | AC | 115 ms | 91864 KiB |
| 03_random2n_05.txt | AC | 116 ms | 93052 KiB |
| 03_random2n_06.txt | AC | 101 ms | 89880 KiB |
| 03_random2n_07.txt | AC | 95 ms | 90340 KiB |
| 03_random2n_08.txt | AC | 118 ms | 93028 KiB |
| 03_random2n_09.txt | AC | 118 ms | 94136 KiB |
| 03_random2n_10.txt | AC | 106 ms | 91632 KiB |
| 03_random2n_11.txt | AC | 108 ms | 92352 KiB |
| 03_random2n_12.txt | AC | 99 ms | 86276 KiB |
| 03_random2n_13.txt | AC | 116 ms | 93888 KiB |
| 03_random2n_14.txt | AC | 112 ms | 88800 KiB |
| 03_random2n_15.txt | AC | 92 ms | 90000 KiB |
| 04_killer_00.txt | AC | 89 ms | 91748 KiB |
| 04_killer_01.txt | AC | 91 ms | 91728 KiB |
| 04_killer_02.txt | AC | 75 ms | 89396 KiB |
| 04_killer_03.txt | AC | 90 ms | 91420 KiB |
| 05_handmade_00.txt | AC | 70 ms | 77268 KiB |
| 05_handmade_01.txt | AC | 55 ms | 76308 KiB |
| 05_handmade_02.txt | AC | 95 ms | 92328 KiB |
| 05_handmade_03.txt | AC | 94 ms | 91976 KiB |
| 05_handmade_04.txt | AC | 97 ms | 92572 KiB |
| 05_handmade_05.txt | AC | 70 ms | 77420 KiB |
| 05_handmade_06.txt | AC | 70 ms | 77284 KiB |
| 05_handmade_07.txt | AC | 70 ms | 77100 KiB |
| 05_handmade_08.txt | AC | 69 ms | 77416 KiB |