Problem:
Problem F: 天使の階段

なつめの住んでいる街の上に浮かんでいる雲には、天使が住んでいる。その天使はなつめと同じくねこ好きで、しばしばねこと遊びに地上に降りてくる。地上に降りるために、天使は雲から地上へ繋がる長い長い階段を作った。しかし、毎回毎回ただ降りていくだけではつまらないと思った天使は、階段に細工をして、段を踏むと音が出るようにした。これを使って、音楽を奏でながら降りていくのだ。

音楽は12種類の音を使って奏でるものである。今回、音のオクターブは無視することにして、 C, C#, D, D#, E, F, F#, G, G#, A, A#, B の12種類のみを考える。隣りあう音同士の差を半音と呼ぶ。たとえば、Cを半音上げるとC#、C#を半音上げるとDとなる。逆に、Gを半音下げるとF#、 F#を半音下げるとFとなる。なお、Eを半音上げるとF、Bを半音上げるとCになることに注意してほしい。

階段から音が出る仕組みは次のようになっている。まず、階段は空中に浮かぶ n 枚の白い板からなっている。雲から数えて 1 ~ n 段目の板には、それぞれに12音のどれかが割り振られている。これを Ti (i = 1 ... n) と書くことにする。また簡単のために、雲は0段目、地上は n+1 段目と考える (ただしこれらには音階は割り当てられていない) 。天使が k (k = 0 ... n) 段目にいるとき、次はk-1, k+1, k+2 段のうち存在するもののどれかに行くことができる。ただし、n+1 段目 (地上) に降りた後は動くことはできず、0段目 (雲) を離れた後はそこに戻ることはできない。それぞれの動き方をしたとき、次のようなルールで音が鳴る。

k+1 段目に降りた

Tk+1 の音が鳴る。

k+2 段目に降りた

Tk+2 を半音上げた音が鳴る。

k-1 段目に戻った

Tk-1 を半音下げた音が鳴る。

階段の情報 T1 ... Tn と、天使が奏でたい曲 S1 ... Sm が与えられる。このとき、奏でたい曲の前、途中、後で別の音を鳴らしてはならない。天使がこの曲を奏でて雲から地上へ降りられるかどうか判定せよ。

Input

入力の1行目には、階段の段数 n と天使が奏でたい曲の長さ m が与えられる。2行目は階段の情報であり、T1, T2, ..., Tn がこの順で与えられる。3行目は天使が奏でたい曲であり、S1, S2, ..., Sm がこの順で与えられる。これらは全て1つの空白文字で区切られており、1 <= n, m <= 50000を満たす。

Output

天使が与えられた曲を奏でながら地上に降りられるなら"Yes"、そうでなければ"No"を1行に出力せよ。

Notes on Submission

上記形式で複数のデータセットが与えられます。入力データの 1 行目にデータセットの数が与えられます。各データセットに対する出力を上記形式で順番に出力するプログラムを作成して下さい。

Sample Input

4
6 4
C E D# F G A
C E F G
6 4
C E D# F G A
C D# F G
3 6
C D D
D# B D B D# C#
8 8
C B B B B B F F
C B B B B B B B

Output for the Sample Input

Yes
No
Yes
No

Solution in JAVA (judge verdict: Memory Limit Exceeded)

import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Scanner;

public class Main {
	
	public static final int SIZE = 12;
	
	public static int next(int m){
		return (m + 1) % SIZE;
	}
	
	public static int prev(int m){
		return (m - 1 + SIZE) % SIZE;
	}
	
	public static int hash(int n, int m){
		return n << 16 + m;
	}
	
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		
		HashMap<String, Integer> map = new HashMap<String, Integer>();
		map.put("C" , 0);
		map.put("C#", 1);
		map.put("D" , 2);
		map.put("D#", 3);
		map.put("E" , 4);
		map.put("F" , 5);
		map.put("F#", 6);
		map.put("G" , 7);
		map.put("G#", 8);
		map.put("A" , 9);
		map.put("A#", 10);
		map.put("B" , 11);
		
		final int T = sc.nextInt();
		
		for(int tt = 0; tt < T; tt++){
			final int n = sc.nextInt();
			final int m = sc.nextInt();
			
			int[] stairs = new int[n];
			int[] hope = new int[m];
			
			for(int i = 0; i < n; i++){
				stairs[i] = map.get(sc.next());
			}
			for(int i = 0; i < m; i++){
				hope[i] = map.get(sc.next());
			}
			
			LinkedList<Integer> cur_pos = new LinkedList<Integer>();
			LinkedList<Integer> perhap   = new LinkedList<Integer>();
			cur_pos.add(-1);
			perhap.add(0);
			
			HashSet<Integer> set = new HashSet<Integer>();
			set.add(hash(-1, 0));
			
			boolean flag = false;
			LOOP:
			while(!cur_pos.isEmpty()){
				final int pos = cur_pos.poll();
				final int purpose = perhap.poll();
				
				//System.out.println(Arrays.toString(stairs));
				//System.out.println(Arrays.toString(hope));
				//System.out.println(pos + " " + purpose);
				
				if(purpose == m){
					if(pos + 1 == n || pos + 2 == n){
						flag = true;
						break LOOP;
					}
				}else{
					if(pos - 1 >= 0 && hope[purpose] == prev(stairs[pos - 1]) && !set.contains(hash(pos - 1, purpose + 1))){
						cur_pos.add(pos - 1);
						perhap.add(purpose + 1);
					}
					
					if(pos + 1 >= 0 && pos + 1 < n && hope[purpose] == stairs[pos + 1] && !set.contains(hash(pos + 1, purpose + 1))){
						cur_pos.add(pos + 1);
						perhap.add(purpose + 1);
					}
					
					if(pos + 2 >= 0 && pos + 2 < n && hope[purpose] == next(stairs[pos + 2]) && !set.contains(hash(pos + 2, purpose + 1))){
						cur_pos.add(pos + 2);
						perhap.add(purpose + 1);
					}
				}
			}
			
			System.out.println(flag ? "Yes" : "No");
		}
	}

}