This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package atcoder.other2016.codefestival2016.qualb; | |
import java.io.IOException; | |
import java.io.InputStream; | |
import java.io.PrintWriter; | |
import java.util.*; | |
public class E { | |
public static void main(String[] args) { | |
InputReader in = new InputReader(System.in); | |
PrintWriter out = new PrintWriter(System.out); | |
int n = in.nextInt(); | |
char[][] s = new char[n][]; | |
for (int i = 0; i < n ; i++) { | |
s[i] = in.nextToken().toCharArray(); | |
} | |
PrefixAutomaton trie = new PrefixAutomaton(s); | |
// 不要な辺を圧縮する | |
trie.compress(); | |
// cnt[head] := head以下にある文字列の数を準備 | |
trie.goup(); | |
int[] ord = new int[26]; | |
int q = in.nextInt(); | |
while (--q >= 0) { | |
int k = in.nextInt()-1; | |
char[] target = s[k]; | |
char[] oc = in.nextToken().toCharArray(); | |
for (int i = 0; i < 26 ; i++) { | |
ord[oc[i]-'a'] = i; | |
} | |
int head = 0; | |
int cnt = 0; | |
while (true) { | |
// cmp[head] := ノード番号 head において何番目の文字を比較するか | |
int cmp = trie.cmp[head]; | |
// この地点で終わってる文字を答えに足す | |
cnt += trie.hereCnt[head]; | |
if (cmp >= target.length) { | |
break; | |
} | |
// 本実装では26回分ループを回しているが、各trie上の各頂点で存在する辺の配列を持っておけば高速化が見込める。 | |
int myindex = ord[target[cmp]-'a']; | |
for (int i = 0 ; i < 26 ; i++) { | |
int nextID = trie.next[head][i]; | |
if (nextID == 0) { | |
continue; | |
} | |
// 自分のインデックスより若い文字列を足す | |
if (ord[i] < myindex) { | |
cnt += trie.cnt[nextID]; | |
} | |
} | |
head = trie.next[head][target[cmp]-'a']; | |
} | |
out.println(cnt); | |
} | |
out.flush(); | |
} | |
// trie | |
static class PrefixAutomaton { | |
int n; | |
char[][] words; | |
int[][] next; | |
int[][] allNext; | |
int[] parentId; | |
int[] lastCharacter; | |
int[] cnt; | |
int[] hereCnt; | |
int[] cmp; | |
int ni; | |
public PrefixAutomaton(char[][] words) { | |
n = 1; | |
this.words = words; | |
for (int i = 0; i < words.length ; i++) { | |
n += words[i].length; | |
} | |
next = new int[n+1][26]; | |
allNext = new int[n+1][26]; | |
parentId = new int[n+1]; | |
lastCharacter = new int[n+1]; | |
cnt = new int[n+1]; | |
hereCnt = new int[n+1]; | |
cmp = new int[n+1]; | |
ni = 1; | |
for (char[] w : words) { | |
add(w); | |
} | |
} | |
private int go(char[] l) { | |
int head = 0; | |
for (int i = 0; i < l.length ; i++) { | |
int idx = cmp[head]; | |
if (idx >= l.length) { | |
break; | |
} | |
head = next[head][l[idx]-'a']; | |
} | |
return head; | |
} | |
private void add(char[] c) { | |
int head = 0; | |
for (int i = 0; i < c.length ; i++) { | |
int ci = c[i]-'a'; | |
if (next[head][ci] == 0) { | |
next[head][ci] = ni++; | |
} | |
parentId[next[head][ci]] = head; | |
cmp[head] = i; | |
head = next[head][ci]; | |
lastCharacter[head] = ci; | |
} | |
cmp[head] = c.length; | |
cnt[head]++; | |
hereCnt[head]++; | |
} | |
public void compress() { | |
for (int i = 1 ; i < ni ; i++) { | |
int has = 0; | |
int onlyID = -1; | |
for (int k = 0 ; k < 26 ; k++) { | |
if (next[i][k] != 0) { | |
onlyID = next[i][k]; | |
has++; | |
} | |
} | |
// 辺が一つしか無い かつ 終端の頂点でもない => 無くても問題ない | |
if (has == 1 && hereCnt[i] == 0) { | |
int my = lastCharacter[i]; | |
next[parentId[i]][my] = onlyID; | |
parentId[onlyID] = parentId[i]; | |
lastCharacter[onlyID] = my; | |
} | |
} | |
} | |
public void goup() { | |
for (int i = ni-1 ; i >= 1 ; i--) { | |
cnt[parentId[i]] += cnt[i]; | |
} | |
} | |
} | |
// 以下テンプレにつき略 | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package atcoder.other2016.codefestival2016.qualb; | |
import java.io.IOException; | |
import java.io.InputStream; | |
import java.io.PrintWriter; | |
import java.util.*; | |
/** | |
* Another solution. | |
*/ | |
public class E2 { | |
public static void main(String[] args) { | |
InputReader in = new InputReader(System.in); | |
PrintWriter out = new PrintWriter(System.out); | |
int n = in.nextInt(); | |
char[][] s = new char[n][]; | |
for (int i = 0; i < n ; i++) { | |
s[i] = in.nextToken().toCharArray(); | |
} | |
PrefixAutomaton trie = new PrefixAutomaton(s); | |
trie.goup(); | |
int q = in.nextInt(); | |
List<Query> queries = new ArrayList<>(); | |
for (int i = 0; i < q ; i++) { | |
int k = in.nextInt()-1; | |
char[] oc = in.nextToken().toCharArray(); | |
int[] ord = new int[26]; | |
for (int oi = 0; oi < 26 ; oi++) { | |
ord[oc[oi]-'a'] = oi; | |
} | |
queries.add(new Query(i, k, ord)); | |
} | |
Collections.sort(queries, (a, b) -> a.sidx - b.sidx); | |
int[] ans = new int[q]; | |
for (int f = 0; f < q ; ) { | |
int t = f; | |
while (t < q && queries.get(f).sidx == queries.get(t).sidx) { | |
t++; | |
} | |
char[] target = s[queries.get(f).sidx]; | |
int[][] table = trie.getTable(target); | |
for (int qi = f ; qi < t ; qi++) { | |
int qid = queries.get(qi).qidx; | |
int[] ord = queries.get(qi).ord; | |
int qa = 0; | |
for (int i = 0 ; i < 26 ; i++) { | |
for (int j = i+1 ; j < 26 ; j++) { | |
if (ord[i] < ord[j]) { | |
// iがjより若いので答えに table[i][j] を足す | |
qa += table[i][j]; | |
} else { | |
// jがiより若いので答えに table[j][i] を足す | |
qa += table[j][i]; | |
} | |
} | |
} | |
// table[0][0] := 自分をprefixに持つ文字列の数 | |
ans[qid] = qa + table[0][0]; | |
} | |
f = t; | |
} | |
for (int i = 0; i < q ; i++) { | |
out.println(ans[i]+1); | |
} | |
out.flush(); | |
} | |
static class Query { | |
int qidx; | |
int sidx; | |
int[] ord; | |
public Query(int q, int s, int[] o) { | |
qidx = q; | |
sidx = s; | |
ord = o; | |
} | |
} | |
static class PrefixAutomaton { | |
int n; | |
char[][] words; | |
int[][] next; | |
int[][] allNext; | |
int[] parentId; | |
int[] lastCharacter; | |
int[] cnt; | |
int[] hereCnt; | |
int[] cmp; | |
int ni; | |
public PrefixAutomaton(char[][] words) { | |
n = 1; | |
this.words = words; | |
for (int i = 0; i < words.length ; i++) { | |
n += words[i].length; | |
} | |
next = new int[n+1][26]; | |
allNext = new int[n+1][26]; | |
parentId = new int[n+1]; | |
lastCharacter = new int[n+1]; | |
cnt = new int[n+1]; | |
hereCnt = new int[n+1]; | |
cmp = new int[n+1]; | |
ni = 1; | |
for (char[] w : words) { | |
add(w); | |
} | |
} | |
// 比較用テーブルを作る | |
public int[][] getTable(char[] l) { | |
// table[x][y] := x が y より若い時、答えに table[x][y] を足し込む | |
int[][] table = new int[26][26]; | |
int head = 0; | |
for (int i = 0; i < l.length ; i++) { | |
// 変な実装方法だけど、自身のprefixとなる文字列の数を table[0][0] に足し込んでいる。 | |
// 比較では table[0][0] は使わないので一応これで動く。 | |
table[0][0] += hereCnt[head]; | |
int idx = cmp[head]; | |
if (idx >= l.length) { | |
break; | |
} | |
int myIdx = l[idx]-'a'; | |
for (int e = 0 ; e < 26 ; e++) { | |
if (next[head][e] != 0 && e != myIdx) { | |
table[e][myIdx] += cnt[next[head][e]]; | |
} | |
} | |
head = next[head][myIdx]; | |
} | |
return table; | |
} | |
private int go(char[] l) { | |
int head = 0; | |
for (int i = 0; i < l.length ; i++) { | |
int idx = cmp[head]; | |
if (idx >= l.length) { | |
break; | |
} | |
head = next[head][l[idx]-'a']; | |
} | |
return head; | |
} | |
private void add(char[] c) { | |
int head = 0; | |
for (int i = 0; i < c.length ; i++) { | |
int ci = c[i]-'a'; | |
if (next[head][ci] == 0) { | |
next[head][ci] = ni++; | |
} | |
parentId[next[head][ci]] = head; | |
cmp[head] = i; | |
head = next[head][ci]; | |
lastCharacter[head] = ci; | |
} | |
cmp[head] = c.length; | |
cnt[head]++; | |
hereCnt[head]++; | |
} | |
public void goup() { | |
for (int i = ni-1 ; i >= 1 ; i--) { | |
cnt[parentId[i]] += cnt[i]; | |
} | |
} | |
} | |
// 以下テンプレにつき略 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment