Skip to content

Instantly share code, notes, and snippets.

@hamadu
Created October 19, 2016 15:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hamadu/8b7a66f8155e1efa4832e3259d6f82df to your computer and use it in GitHub Desktop.
Save hamadu/8b7a66f8155e1efa4832e3259d6f82df to your computer and use it in GitHub Desktop.
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];
}
}
}
// 以下テンプレにつき略
}
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