在m进制下两数L、R,给出若干子串(m进制),每个子串有一个权值。一个m进制数的辣度值为该数的字符串表示中出现已给子串的权值和,子串可以重复,即一个子串可以出现多次。
对给出的子串建立AC自动机,求任意数的辣度值在AC自动机上走一遍即可。所以在AC自动机上进行数位DP即可,DP[len][node][k][upper]表示自动机上接收长度为len,节点为node,辣度值为k,且接收字符串是否为模板的前len位时,串的个数。有m中转移方式到接受长度为len+1的状态。
import java.util.*;
import java.math.*;
import java.io.*;
public class Main {
static int mod = 1000000007;
static AC ac;
static int n, m, K;
static int[] readnum() throws IOException {
int len = Reader.nextInt();
int[] ret = new int[len];
for (int i=0; i<len; i++)
ret[i] = Reader.nextInt();
return ret;
}
public static void main(String[] args) throws IOException {
// System.setIn(new FileInputStream("injava.txt"));
Reader.init(System.in);
n = Reader.nextInt();
m = Reader.nextInt();
K = Reader.nextInt();
int[] L = readnum();
int[] R = readnum();
ac = new AC(m);
for (int i=0; i<n; i++) {
int[] x = readnum();
int val = Reader.nextInt();
ac.insert(x, x.length, val);
}
ac.build();
L[L.length - 1]--;
for (int i=L.length-1; i>=0; i--) {
if (L[i] < 0) {
L[i] = m - 1;
L[i-1]--;
}
}
if (L[0] == 0)
L = Arrays.copyOfRange(L, 1, L.length);
System.out.println((ac.getans(R, n, m, K) - ac.getans(L, n, m, K) + mod) % mod);;
}
}
class AC {
int m, cnt;
class Node{
Node[] go;
Node fail;
int val, id;
Node() {
go = new Node[m];
fail = null;
val = 0;
id = cnt++;
}
}
Node root;
Node[] node;
AC(int _m) {
m = _m;
root = new Node();
}
void insert(int[] a, int len, int val) {
Node now = root;
for (int i=0; i<len; i++) {
if (now.go[a[i]] == null)
now.go[a[i]] = new Node();
now = now.go[a[i]];
}
now.val += val;
}
void build() {
node = new Node[cnt];
node[root.id] = root;
root.fail = root;
Queue<Node> que = new LinkedList<Node>();
for (int i=0; i<m; i++) {
if (root.go[i] == null)
root.go[i] = root;
else {
root.go[i].fail = root;
que.add(root.go[i]);
}
}
while (que.size() > 0) {
Node now = que.poll();
node[now.id] = now;
now.val += now.fail.val;
for (int i=0; i<m; i++) {
if (now.go[i] == null) {
now.go[i] = now.fail.go[i];
} else {
now.go[i].fail = now.fail.go[i];
que.add(now.go[i]);
}
}
}
}
int getans(int a[], int n, int m, int K) {
if (a.length == 0) return 0;
final int mod = 1000000007;
int len = a.length;
int[][][][] dp = new int[len+1][cnt][K+1][2];
for (int i=1; i<a[0]; i++) {
if (root.go[i].val <= K) {
dp[1][root.go[i].id][root.go[i].val][0]++;
if (dp[1][root.go[i].id][root.go[i].val][0] >= mod)
dp[1][root.go[i].id][root.go[i].val][0] -= mod;
}
}
if (root.go[a[0]].val <= K) {
dp[1][root.go[a[0]].id][root.go[a[0]].val][1]++;
if (dp[1][root.go[a[0]].id][root.go[a[0]].val][1] >= mod)
dp[1][root.go[a[0]].id][root.go[a[0]].val][1] -= mod;
}
for (int l=1; l<len; l++) {
for (int i=1; i<m; i++) {
if (root.go[i].val <= K) {
dp[l+1][root.go[i].id][root.go[i].val][0]++;
if (dp[l+1][root.go[i].id][root.go[i].val][0] >= mod)
dp[l+1][root.go[i].id][root.go[i].val][0] -= mod;
}
}
for (int c=0; c<cnt; c++) {
for (int k=0; k<=K; k++) {
for (int u=0; u<2; u++) {
if (dp[l][c][k][u] == 0)
continue;
for (int i=0; i<m; i++) {
if (u == 1 && i > a[l])
break;
int uto = (u == 1 && i == a[l]) ? 1 : 0;
int kto = k + node[c].go[i].val;
int cto = node[c].go[i].id;
if (kto > K)
continue;
dp[l+1][cto][kto][uto] += dp[l][c][k][u];
if (dp[l+1][cto][kto][uto] >= mod)
dp[l+1][cto][kto][uto] -= mod;
}
}
}
}
}
int ret = 0;
for (int c=0; c<cnt; c++) {
for (int k=0; k<=K; k++) {
for (int u=0; u<2; u++) {
ret += dp[len][c][k][u];
if (ret >= mod)
ret -= mod;
}
}
}
return ret;
}
}
class Reader {
static BufferedReader reader;
static StringTokenizer tokenizer;
static void init(InputStream input) {
reader = new BufferedReader(new InputStreamReader(input));
tokenizer = new StringTokenizer("");
}
static String next() throws IOException {
while (!tokenizer.hasMoreTokens()) {
tokenizer = new StringTokenizer(reader.readLine());
}
return tokenizer.nextToken();
}
static int nextInt() throws IOException {
return Integer.parseInt(next());
}
static double nextDouble() throws IOException {
return Double.parseDouble(next());
}
static long nextLong() throws IOException {
return Long.parseLong(next());
}
static boolean isend() throws IOException {
if (tokenizer.hasMoreTokens())
return false;
do {
String s = reader.readLine();
if (s == null)
return true;
tokenizer = new StringTokenizer(s);
} while (!tokenizer.hasMoreTokens());
return false;
}
}