Problem:
問題 E : Pattern Language
問題文
m
個の相異なるアルファベット
var_0,  var_1,   … ,  var_{m-1}
がある．
0,   1,   … ,   9,   var_0,   var_1,   … ,   var_{m-1}
の
10+m
種類の文字からなる，長さ
N
の文字列
s_0s_1s_2…s_{N-1}
が与えられる．
この文字列における各アルファベットを数字で置き換えて回文になるようにしたい．(回文とは，前から読んでも後ろから読んでも同じ文字列をあらわす．)
ここで，同じアルファベットは同じ数字で置き換えなければならない．また与えられたすべてのアルファベット
var_i
は少なくとも，一度は文字列
s_0s_1s_2…s_{N-1}
にあらわれる．
アルファベット
var_i
は
0
以上
u_i
以下の，leading zero を含まない整数に置き換える事ができる．
置き換えた後の文字列が回文になるような置き換え方が何通り存在するかを，
mod
10^9+7
で求めよ．
なお，アルファベットの置き換え方が異なれば，得られる文字列が同じでも異なるものとして数える．
入力形式
入力は以下の形式で与えられる
N
m
s_0s_1s_2…s_{N-1}
var_0
u_0
...
var_{m-1}
u_{m-1}
出力形式
置き換え方の場合の数を
10^9 + 7
で割った剰余を一行で出力せよ．
制約
1 ≤ N ≤ 500
1 ≤ m ≤ 10
0 ≤ u_i ≤ 99
s_i
∈
\{'0',   '1',   … ,   '9',   var_0,   var_1,   … ,   var_{m-1}\}
var_i ∈ \{'a',   'b',   … ,   'j'\}
各アルファベット
var_i
は
s_0s_1s_2 …s_{N-1}
に少なくとも一度は現れる．
var_0,  var_1,  … ,  var_{m-1}
はすべて異なる
入出力例
入力例 1
3 1
a1a
a 99
出力例 1
19
入力例 2
5 3
jbfjb
f 50
b 25
j 5
出力例 2
252
入力例 3
7 3
jag2013
j 53
a 10
g 93
出力例 3
23

Submitted solutions:

code_snippet: // AOJ #2604 Pattern Language
// 2025.2.27

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const ll MOD = 1000000007;

struct UF {
    vector<int> parent;
    vector<int> fixedVal;
    vector<bool> nonzero;
    UF(int n) {
        parent.resize(n);
        fixedVal.assign(n, -1);
        nonzero.assign(n, false);
        for (int i = 0; i < n; i++) parent[i] = i;
    }
    int find(int a) {
        return parent[a] = (parent[a] == a ? a : find(parent[a]));
    }
    bool merge(int a, int b) {
        a = find(a), b = find(b);
        if(a == b) return true;
        bool nz = nonzero[a] || nonzero[b];
        int fv = -1;
        if(fixedVal[a] != -1 && fixedVal[b] != -1) {
            if(fixedVal[a] != fixedVal[b]) return false;
            fv = fixedVal[a];
        } else if(fixedVal[a] != -1) fv = fixedVal[a];
        else if(fixedVal[b] != -1) fv = fixedVal[b];
        if(nz && fv == 0) return false;
        parent[b] = a;
        nonzero[a] = nz;
        fixedVal[a] = fv;
        return true;
    }
    bool setFixed(int a, int val) {
        a = find(a);
        if(nonzero[a] && val == 0) return false;
        if(fixedVal[a] == -1) { fixedVal[a] = val; return true; }
        return (fixedVal[a] == val);
    }
    void setNonZero(int a) {
        a = find(a);
        nonzero[a] = true;
    }
};

struct Slot {
    bool isFixed;
    int fixedDigit;
    int var;
    int pos;
};

int main(){
    ios::sync_with_stdio(0);
    cin.tie(0);

    int N, m;
    cin >> N >> m;
    string s;
    cin >> s;
    vector<int> u(m, 0);
    unordered_map<char,int> varIndex;
    vector<char> varChars(m);
    for (int i = 0; i < m; i++){
        char c; int bound;
        cin >> c >> bound;
        varIndex[c] = i;
        varChars[i] = c;
        u[i] = bound;
    }

    ll ans = 0;
    int maxMask = 1 << m;
    for (int mask = 0; mask < maxMask; mask++){
        bool validMask = true;
        vector<int> lengthChoice(m, 0);
        for (int i = 0; i < m; i++){
            if(mask & (1 << i)){
                if(u[i] < 10){ validMask = false; break; }
                lengthChoice[i] = 2;
            } else {
                lengthChoice[i] = 1;
            }
        }
        if(!validMask) continue;

        vector<Slot> F;
        vector<vector<int>> varNode(m);
        int totalNodes = 0;
        for (int i = 0; i < m; i++){
            int len = lengthChoice[i];
            varNode[i].resize(len, -1);
            for (int j = 0; j < len; j++){
                varNode[i][j] = totalNodes++;
            }
        }

        for (int pos = 0; pos < N; pos++){
            char c = s[pos];
            if(isdigit(c)){
                Slot slot;
                slot.isFixed = true;
                slot.fixedDigit = c - '0';
                slot.var = -1;
                slot.pos = -1;
                F.push_back(slot);
            } else {
                int vid = varIndex[c];
                int len = lengthChoice[vid];
                for (int j = 0; j < len; j++){
                    Slot slot;
                    slot.isFixed = false;
                    slot.fixedDigit = -1;
                    slot.var = vid;
                    slot.pos = j;
                    F.push_back(slot);
                }
            }
        }
        int Lfinal = F.size();

        UF uf(totalNodes);
        for (int i = 0; i < m; i++){
            if(lengthChoice[i] == 2) uf.setNonZero(varNode[i][0]);
        }

        bool conflict = false;
        for (int i = 0; i < Lfinal; i++){
            int j = Lfinal - 1 - i;
            if(i > j) break;
            if(i == j) continue;
            Slot &s1 = F[i], &s2 = F[j];
            if(s1.isFixed && s2.isFixed){
                if(s1.fixedDigit != s2.fixedDigit){ conflict = true; break; }
            } else if(s1.isFixed && !s2.isFixed){
                int node = varNode[s2.var][s2.pos];
                if(!uf.setFixed(node, s1.fixedDigit)){ conflict = true; break; }
            } else if(!s1.isFixed && s2.isFixed){
                int node = varNode[s1.var][s1.pos];
                if(!uf.setFixed(node, s2.fixedDigit)){ conflict = true; break; }
            } else {
                int node1 = varNode[s1.var][s1.pos];
                int node2 = varNode[s2.var][s2.pos];
                if(!uf.merge(node1, node2)){ conflict = true; break; }
            }
        }
        if(conflict) continue;

        unordered_map<int, pair<int,int>> rangeMap;
        unordered_map<int,bool> isFixed;
        for (int i = 0; i < totalNodes; i++){
            int r = uf.find(i);
            if(rangeMap.find(r) == rangeMap.end()){
                if(uf.fixedVal[r] != -1){
                    rangeMap[r] = {uf.fixedVal[r], uf.fixedVal[r]};
                    isFixed[r] = true;
                } else {
                    int lo = (uf.nonzero[r] ? 1 : 0);
                    rangeMap[r] = {lo, 9};
                    isFixed[r] = false;
                }
            }
        }

        struct PairConstraint { int compT, compO, limit; };
        vector<PairConstraint> pairConstraints;
        bool ineqConflict = false;
        for (int i = 0; i < m; i++){
            if(lengthChoice[i] == 1){
                int r = uf.find(varNode[i][0]);
                int ub = min(9, u[i]);
                auto &pr = rangeMap[r];
                pr.second = min(pr.second, ub);
                if(pr.first > pr.second){ ineqConflict = true; break; }
            } else {
                int rT = uf.find(varNode[i][0]);
                int rO = uf.find(varNode[i][1]);
                if(rT == rO){
                    int ub = u[i] / 11;
                    auto &pr = rangeMap[rT];
                    pr.second = min(pr.second, ub);
                    if(pr.first > pr.second){ ineqConflict = true; break; }
                } else {
                    bool fixedT = isFixed[rT], fixedO = isFixed[rO];
                    if(fixedT && fixedO){
                        int dT = rangeMap[rT].first, dO = rangeMap[rO].first;
                        if(10 * dT + dO > u[i]) { ineqConflict = true; break; }
                    } else if(fixedT && !fixedO){
                        int dT = rangeMap[rT].first;
                        int ub = u[i] - 10 * dT;
                        auto &pr = rangeMap[rO];
                        pr.second = min(pr.second, ub);
                        if(pr.first > pr.second){ ineqConflict = true; break; }
                    } else if(!fixedT && fixedO){
                        int dO = rangeMap[rO].first;
                        int ub = (u[i] - dO) / 10;
                        auto &pr = rangeMap[rT];
                        pr.second = min(pr.second, ub);
                        if(pr.first > pr.second){ ineqConflict = true; break; }
                    } else pairConstraints.push_back({rT, rO, u[i]});
                }
            }
        }
        if(ineqConflict) continue;

        vector<int> freeComps;
        for(auto &entry : rangeMap){
            int r = entry.first;
            if(!isFixed[r]) freeComps.push_back(r);
        }
        sort(freeComps.begin(), freeComps.end());
        int freeCount = freeComps.size();
        unordered_map<int,int> compIndex;
        for (int i = 0; i < freeCount; i++) compIndex[ freeComps[i] ] = i;

        vector<tuple<int,int,int>> pairCons;
        for(auto &pc : pairConstraints){
            if(compIndex.find(pc.compT) != compIndex.end() && compIndex.find(pc.compO) != compIndex.end()){
                int idxT = compIndex[pc.compT], idxO = compIndex[pc.compO];
                pairCons.push_back({idxT, idxO, pc.limit});
            }
        }

        vector<pair<int,int>> freeRanges(freeCount);
        for (int i = 0; i < freeCount; i++){
            int r = freeComps[i];
            freeRanges[i] = rangeMap[r];
        }

        unordered_map<ll, ll> dp;
        function<ll(int, ll)> dfs = [&](int idx, ll state) -> ll {
            ll key = (((ll)idx) << 40) | state;
            if(dp.count(key)) return dp[key];
            if(idx == freeCount){
                vector<int> assign(freeCount);
                ll temp = state;
                for (int i = freeCount - 1; i >= 0; i--){
                    assign[i] = temp & 0xF;
                    temp >>= 4;
                }
                for(auto &pc : pairCons){
                    int iT, iO, lim;
                    tie(iT, iO, lim) = pc;
                    if(10 * assign[iT] + assign[iO] > lim) return dp[key] = 0LL;
                }
                return dp[key] = 1LL;
            }
            ll ways = 0;
            int lo = freeRanges[idx].first, hi = freeRanges[idx].second;
            for (int d = lo; d <= hi; d++){
                bool ok = true;
                vector<int> assign(idx+1);
                ll temp = state;
                for (int i = idx - 1; i >= 0; i--){
                    assign[i] = temp & 0xF;
                    temp >>= 4;
                }
                assign[idx] = d;
                for(auto &pc : pairCons){
                    int iT, iO, lim;
                    tie(iT, iO, lim) = pc;
                    if(iT == idx && iO < idx){
                        if(10 * d + assign[iO] > lim){ ok = false; break; }
                    } else if(iO == idx && iT < idx){
                        if(10 * assign[iT] + d > lim){ ok = false; break; }
                    }
                }
                if(!ok) continue;
                ll newState = (state << 4) | d;
                ways = (ways + dfs(idx+1, newState)) % MOD;
            }
            dp[key] = ways;
            return ways;
        };
        ans = (ans + dfs(0, 0)) % MOD;
    }
    cout << ans << endl;
    return 0;
}
accuracy: 1
time_ms: 600
memory_kb: 30984
final_rank: 18

code_snippet: #include <bits/stdc++.h>

using namespace std;

#define rep(i, a, b) for (int i = (int)(a); i < (int)(b); i++)
#define rrep(i, a, b) for (int i = (int)(b)-1; i >= (int)(a); i--)
#define ALL(v) (v).begin(), (v).end()
#define UNIQUE(v) sort(ALL(v)), (v).erase(unique(ALL(v)), (v).end())
#define SZ(v) (int)v.size()
#define MIN(v) *min_element(ALL(v))
#define MAX(v) *max_element(ALL(v))
#define LB(v, x) int(lower_bound(ALL(v), (x)) - (v).begin())
#define UB(v, x) int(upper_bound(ALL(v), (x)) - (v).begin())

using uint = unsigned int;
using ll = long long int;
using ull = unsigned long long;
using i128 = __int128_t;
using u128 = __uint128_t;
const int inf = 0x3fffffff;
const ll INF = 0x1fffffffffffffff;

template <typename T> inline bool chmax(T &a, T b) {
    if (a < b) {
        a = b;
        return 1;
    }
    return 0;
}
template <typename T> inline bool chmin(T &a, T b) {
    if (a > b) {
        a = b;
        return 1;
    }
    return 0;
}
template <typename T, typename U> T ceil(T x, U y) {
    assert(y != 0);
    if (y < 0)
        x = -x, y = -y;
    return (x > 0 ? (x + y - 1) / y : x / y);
}
template <typename T, typename U> T floor(T x, U y) {
    assert(y != 0);
    if (y < 0)
        x = -x, y = -y;
    return (x > 0 ? x / y : (x - y + 1) / y);
}
template <typename T> int popcnt(T x) {
    return __builtin_popcountll(x);
}
template <typename T> int topbit(T x) {
    return (x == 0 ? -1 : 63 - __builtin_clzll(x));
}
template <typename T> int lowbit(T x) {
    return (x == 0 ? -1 : __builtin_ctzll(x));
}

template <class T, class U>
ostream &operator<<(ostream &os, const pair<T, U> &p) {
    os << "P(" << p.first << ", " << p.second << ")";
    return os;
}
template <typename T> ostream &operator<<(ostream &os, const vector<T> &vec) {
    os << "{";
    for (int i = 0; i < vec.size(); i++) {
        os << vec[i] << (i + 1 == vec.size() ? "" : ", ");
    }
    os << "}";
    return os;
}
template <typename T, typename U>
ostream &operator<<(ostream &os, const map<T, U> &map_var) {
    os << "{";
    for (auto itr = map_var.begin(); itr != map_var.end(); itr++) {
        os << "(" << itr->first << ", " << itr->second << ")";
        itr++;
        if (itr != map_var.end())
            os << ", ";
        itr--;
    }
    os << "}";
    return os;
}
template <typename T> ostream &operator<<(ostream &os, const set<T> &set_var) {
    os << "{";
    for (auto itr = set_var.begin(); itr != set_var.end(); itr++) {
        os << *itr;
        ++itr;
        if (itr != set_var.end())
            os << ", ";
        itr--;
    }
    os << "}";
    return os;
}
#ifdef LOCAL
#define show(...) _show(0, #__VA_ARGS__, __VA_ARGS__)
#else
#define show(...) true
#endif
template <typename T> void _show(int i, T name) {
    cerr << '\n';
}
template <typename T1, typename T2, typename... T3>
void _show(int i, const T1 &a, const T2 &b, const T3 &...c) {
    for (; a[i] != ',' && a[i] != '\0'; i++)
        cerr << a[i];
    cerr << ":" << b << " ";
    _show(i + 1, a, c...);
}

/**
 * @brief template
 */

struct UnionFind{
    vector<int> par; int n;
    UnionFind(){}
    UnionFind(int _n):par(_n,-1),n(_n){}
    int root(int x){return par[x]<0?x:par[x]=root(par[x]);}
    bool same(int x,int y){return root(x)==root(y);}
    int size(int x){return -par[root(x)];}
    bool unite(int x,int y){
        x=root(x),y=root(y); if(x==y)return false;
        if(size(x)>size(y))swap(x,y);
        par[y]+=par[x]; par[x]=y; n--; return true;
    }
};

/**
 * @brief Union Find
 */

int main() {
    ll MOD = 1000000007;
    int N, M;
    cin >> N >> M;
    string S;
    cin >> S;
    vector<char> var(M);
    vector<int> U(M);
    map<char,int> mp;
    rep(i,0,M) cin >> var[i] >> U[i], mp[var[i]] = i;
    vector<int> A(N);
    rep(i,0,N) {
        if (isdigit(S[i])) A[i] = S[i]-'0';
        else A[i] = mp[S[i]]+10;
    }
    int D = 1;
    rep(i,0,M) D *= 3;
    ll ANS = 0;
    rep(_,0,D) {
        vector<int> B(M);
        int __ = _;
        rep(i,0,M) B[i] = __ % 3, __ /= 3;
        bool check = true;
        rep(i,0,M) {
            if (B[i] >= 1 && U[i] <= 9) check = false;
            if (B[i] == 1 && U[i] <= 19) check = false;
        }
        if (!check) continue;
        vector<pair<int,int>> Range(10+M*2);
        rep(i,0,10) Range[i] = {i,i};
        rep(i,0,M) {
            if (B[i] == 0) {
                if (U[i] <= 9) Range[10+i*2] = {0,U[i]};
                else Range[10+i*2] = {0,9};
                Range[10+i*2+1] = {0,0};
            }
            if (B[i] == 1) {
                Range[10+i*2] = {0,9};
                Range[10+i*2+1] = {1,U[i]/10-1};
            }
            if (B[i] == 2) {
                Range[10+i*2] = {0,U[i]%10};
                Range[10+i*2+1] = {U[i]/10,U[i]/10};
            }
        }
        vector<int> V;
        rep(i,0,N) {
            if (A[i] < 10) V.push_back(A[i]);
            else {
                int ID = A[i]-10;
                if (B[ID] == 0) V.push_back(10+ID*2);
                else V.push_back(10+ID*2+1), V.push_back(10+ID*2);
            }
        }
        UnionFind UF(10+M*2);
        int L = V.size();
        rep(i,0,L) {
            UF.unite(V[i],V[L-1-i]);
        }
        rep(i,0,10+M*2) {
            chmax(Range[UF.root(i)].first, Range[i].first);
            chmin(Range[UF.root(i)].second, Range[i].second);
        }
        ll Mul = 1;
        rep(i,0,10+M*2) {
            if (UF.root(i) != i) continue;
            if (Range[i].first > Range[i].second) Mul = 0;
            else Mul *= (Range[i].second-Range[i].first+1);
            Mul %= MOD;
        }
        ANS += Mul;
        ANS %= MOD;
    }
    cout << ANS << endl;
}
accuracy: 1
time_ms: 230
memory_kb: 3452
final_rank: 8

code_snippet: #include <iostream>
#include <vector>
#include <map>
#include <bitset>
#include <algorithm>
using namespace std;

typedef long long ll;

const ll MOD = 1e9 + 7;
const int MAX_N = 500;
const int MAX_M = 10;

int parent[30];
int N, M, upperBound[MAX_M];
string s;
char var[MAX_M];
int d[MAX_M] = {};
bool t[MAX_M] = {};
map<char, int> charToIndex;
ll result = 0;

void initDSU(int N) {
    for (int i = 0; i < N; ++i) {
        parent[i] = i;
    }
}

int find(int x) {
    if (parent[x] == x) {
        return x;
    }
    return parent[x] = find(parent[x]);
}

void unite(int x, int y) {
    x = find(x), y = find(y);
    parent[x] = y;
}

ll calculate() {
    vector<int> vc;
    bool use[30] = {};

    for (int i = 0; i < 10; ++i) {
        use[i] = true;
    }

    for (int i = 0; i < N; ++i) {
        if (isdigit(s[i])) {
            vc.push_back(s[i] - '0');
        } else {
            int id = charToIndex[s[i]];
            if (t[id]) {
                vc.push_back(upperBound[id] / 10);
                vc.push_back(10 + id);
                use[10 + id] = true;
            } else if (d[id] == 2) {
                vc.push_back(20 + id);
                vc.push_back(10 + id);
                use[20 + id] = use[10 + id] = true;
            } else {
                vc.push_back(10 + id);
                use[10 + id] = true;
            }
        }
    }

    int sz = vc.size();
    initDSU(30);

    for (int i = 0; i < sz / 2; ++i) {
        unite(vc[i], vc[sz - 1 - i]);
    }

    vector<int> l(30, 0), r(30, 9);

    for (int i = 0; i < 30; ++i) {
        if (!use[i]) {
            continue;
        }
        int x = find(i);

        if (i < 10) {
            l[x] = max(l[x], i);
            r[x] = min(r[x], i);
        } else if (i < 20) {
            int id = i - 10;
            if (t[id]) {
                r[x] = min(r[x], upperBound[id] % 10);
            } else if (d[id] == 2) {
                // Do nothing
            } else {
                r[x] = min(r[x], upperBound[id]);
            }
        } else {
            int id = i - 20;
            if (t[id]) {
                // Do nothing
            } else if (d[id] == 2) {
                l[x] = max(l[x], 1);
                r[x] = min(r[x], upperBound[id] / 10 - 1);
            }
        }
    }

    ll res = 1;
    for (int i = 0; i < 30; ++i) {
        if (!use[i] || i != find(i)) {
            continue;
        }
        if (l[i] > r[i]) {
            return 0;
        }
        res *= (r[i] - l[i] + 1);
        res %= MOD;
    }
    return res;
}

int main() {
    cin >> N >> M >> s;

    for (int i = 0; i < M; ++i) {
        cin >> var[i] >> upperBound[i];
        charToIndex[var[i]] = i;
    }

    for (int i = 0; i < (1 << M); ++i) {
        for (int j = 0; j < M; ++j) {
            d[j] = ((i >> j) & 1) + 1;
        }

        bool isValid = true;
        for (int j = 0; j < M; ++j) {
            if (d[j] == 2 && upperBound[j] < 10) {
                isValid = false;
            }
        }
        if (!isValid) {
            continue;
        }

        for (int j = 0; j < (1 << M); ++j) {
            for (int k = 0; k < M; ++k) {
                t[k] = (j >> k) & 1;
            }

            bool isValid2 = true;
            for (int k = 0; k < M; ++k) {
                if (t[k] && d[k] == 1) {
                    isValid2 = false;
                }
            }
            if (!isValid2) {
                continue;
            }

            result += calculate();
            if (result >= MOD) {
                result -= MOD;
            }
        }
    }

    cout << result << endl;

    return 0;
}
accuracy: 1
time_ms: 580
memory_kb: 3444
final_rank: 13

code_snippet: #pragma GCC optimize("Ofast")
#include <bits/stdc++.h>
using namespace std;
typedef long long int ll;

constexpr ll mod = 1e9+7;

struct UnionFind{
    vector<int> par,num;
    UnionFind(int n):par(n),num(n,1){
        for(int i=0;i<n;i++){
            par[i] = i;
        }
    }
    int find(int v){
        return (par[v] == v)?v:(par[v] = find(par[v]));
    }
    void unite(int v,int u){
        u = find(u), v = find(v);
        if(u == v) return;
        if(num[u] < num[v]) swap(u,v);
        num[u] += num[v];
        par[v] = u;
    }
    bool same(int u,int v){
        return find(u) == find(v);
    }
};

int main() {
    cin.tie(nullptr);
    ios::sync_with_stdio(false);
    int n,m; cin >> n >> m;
    vector<int> a(n);
    vector<int> v(m);
    {
        string s; cin >> s;
        string t = "";
        vector<char> c(m);
        vector<int> vv(m);
        for(int i=0;i<m;i++){
            cin >> c[i] >> vv[i];
            t += c[i];
        }
        sort(t.begin(), t.end());
        for(int i=0;i<n;i++){
            if('0' <= s[i] and s[i] <= '9'){
                a[i] = 20+(s[i]-'0');
            }
            else{
                a[i] = lower_bound(t.begin(), t.end(), s[i]) - t.begin();
            }
        }
        for(int i=0;i<m;i++){
            int j = lower_bound(t.begin(), t.end(), c[i]) - t.begin();
            v[j] = vv[i];
        }
    }
    ll res = 0;
    vector<int> sta(m);
    auto dfs = [&](auto dfs,int idx)->void{
        if(idx == m){
            UnionFind uf(2*m);
            int b = n-1;
            bool f = false;
            vector<int> lim(2*m,-1);
            for(int i=0;i<n;i++){
                int x = a[i];
                // 10の位
                if(x < 20 and sta[x] != 0){
                    int y = a[b];
                    if(y >= 20){
                        if(lim[x+m] == -1) lim[x+m] = y-20;
                        if(lim[x+m] != y-20) return;
                        b--;
                    }
                    else if(!f){ // a[b]の１の位
                        uf.unite(x+m, y);
                        if(sta[y] != 0) f = true;
                        else b--;
                    }
                    else{ // a[b]の10の位
                        uf.unite(x+m, y+m);
                        b--; f = false;
                    }
                }
                // 1の位
                if(x < 20){
                    int y = a[b];
                    if(y >= 20){
                        if(lim[x] == -1) lim[x] = y-20;
                        if(lim[x] != y-20) return;
                        b--;
                    }
                    else if(!f){ // a[b]の１の位
                        uf.unite(x, y);
                        if(sta[y] != 0) f = true;
                        else b--;
                    }
                    else{ // a[b]の10の位
                        uf.unite(x, y+m);
                        b--; f = false;
                    }
                }
                else{
                    int y = a[b];
                    if(y >= 20){
                        if(x != y) return;
                        b--;
                    }
                    else if(!f){ // a[b]の１の位
                        if(lim[y] == -1) lim[y] = x-20;
                        if(lim[y] != x-20) return;
                        if(sta[y] != 0) f = true;
                        else b--;
                    }
                    else{ // a[b]の10の位
                        if(lim[y+m] == -1) lim[y+m] = x-20;
                        if(lim[y+m] != x-20) return;
                        b--; f = false;
                    }
                }
            }

            vector<int> l(2*m,0);
            vector<int> r(2*m,9);
            for(int i=0;i<m;i++){
                if(lim[i] != -1){
                    int x = uf.find(i);
                    if(l[x] <= lim[i] and lim[i] <= r[x]){
                        l[x] = r[x] = lim[i];
                    }
                    else return;
                }
                if(lim[i+m] != -1){
                    int x = uf.find(i+m);
                    if(l[x] <= lim[i+m] and lim[i+m] <= r[x]){
                        l[x] = r[x] = lim[i+m];
                    }
                    else return;
                }
                if(sta[i] == 0){
                    int y = uf.find(i);
                    if(v[i] < 10){
                        r[y] = min(r[y], v[i]);
                    }
                }
                else if(sta[i] == 1){
                    if(v[i] <= 19) return;
                    // [1,v[i]/10)
                    int x = uf.find(i+m);
                    l[x] = max(l[x], 1);
                    r[x] = min(r[x], v[i]/10-1);
                }
                else{
                    if(v[i] <= 9) return;
                    int x = uf.find(i+m);
                    // v[i]/10
                    if(l[x] <= v[i]/10 and v[i]/10 <= r[x]){
                        l[x] = r[x] = v[i]/10;
                    }
                    else return;
                    int y = uf.find(i);
                    r[y] = min(r[y], v[i]%10);
                }
            }
            ll ad = 1;
            for(int i=0;i<2*m;i++){
                if(uf.find(i) == i){
                    if(m <= i and sta[i-m] == 0) continue;
                    if(r[i] < l[i]) return;
                    ad *= (r[i]-l[i]+1);
                    ad %= mod;
                }
            }
            res += ad;
            return;
        }
        for(int i=0;i<3;i++){
            sta[idx] = i;
            dfs(dfs,idx+1);
        }
    };
    dfs(dfs,0);
    cout << res%mod << endl;
}
accuracy: 1
time_ms: 280
memory_kb: 3452
final_rank: 9

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ll = long long;
using vi = vector<int>;
using pii = pair<int, int>;
#define rep(i, x)    for(int i = 0; i < (x); i++)
#define rng(i, l, r) for(int i = (l); i < (r); i++)
#define all(v)       (v).begin(), (v).end()
#define sz(v)        (int)(v).size()

// uf 欲しい
struct uf {
	vi e;
	uf(int n) : e(n, -1) {}
	bool same(int u, int v) { return find(u) == find(v); }
	int size(int x) { return -e[find(x)]; }
	int find(int x) { return e[x] < 0 ? x : e[x] = find(e[x]); }
	bool unite(int a, int b) {
		a = find(a), b = find(b);
		if(a == b) return false;
		if(e[a] > e[b]) swap(a, b);
		e[a] += e[b];
		e[b] = a;
		return true;
	}
};
const int mod = 1000000007;
int main() {
	int n, m;
	cin >> n >> m;
	string s;
	cin >> s;
	vector<char> cs;
	map<char, int> mp;
	vi u;
	rep(i, m) {
		char c;
		int x;
		cin >> c >> x;
		mp[c] = i;
		cs.emplace_back(c);
		u.emplace_back(x);
	}
	vector<int> id(n, -1);
	rep(i, n) {
		if(mp.count(s[i])) id[i] = mp[s[i]];
	}
	ll res = 0;
	// a
	rep(bit, 1 << m) {
		auto en = [&](int i, int t) {
			return 10 + i * 2 + t;
		};
		//有効な変数を入れておく
		vi vars;
		bool out = 0;
		vector<int> twos;
		rep(i, m) {
			if(bit >> i & 1 and u[i] < 10) {
				out = 1;
				break;
			}
			if(bit >> i & 1) {
				twos.push_back(i);
				vars.push_back(en(i, 1));
			}
			vars.push_back(en(i, 0));
		}
		if(out) continue;
		vector<int> v;
		// i:13(i, 1)-> 1 (i, 0)->3
		//実装が下手で困った

		rep(i, n) {
			if(id[i] == -1)
				v.push_back(s[i] - '0');
			else {
				if(bit >> id[i] & 1) {
					v.push_back(en(id[i], 1));
					v.push_back(en(id[i], 0));
				} else {
					v.push_back(en(id[i], 0));
				}
			}
		}
		uf uf(30);
		// dousiyou
		rep(i, sz(v) / 2) {
			int l = v[i];
			int r = v[sz(v) - 1 - i];
			if(l >= 10 and r >= 10) {
				uf.unite(l, r);
			}
			if(l < 10 and r < 10 and l != r) {
				out = 1;
				break;
			}
		}
		if(out) continue;
		vector<vi> gr(30);
		rep(i, 30) {
			gr[uf.find(i)].push_back(i);
		}
		vector<int> Ika(30, 9), Eq(30, -1);
		rep(i, sz(v) / 2) {
			int l = v[i];
			int r = v[sz(v) - 1 - i];
			if(l > r) swap(l, r);
			if(l < 10 and r >= 10) {
				if(Eq[uf.find(r)] != -1 and Eq[uf.find(r)] != l) {
					out = 1;
					break;
				}
				Eq[uf.find(r)] = l;
			}
		}
		if(out) continue;
		rep(b2, 1 << sz(twos)) {
			out = 0;
			auto ika = Ika;
			auto eq = Eq;
			rep(I, sz(twos)) {
				if(b2 >> I & 1) {
					int i = twos[I];
					int tar = u[i] / 10;
					if(eq[uf.find(en(i, 1))] != -1 and eq[uf.find(en(i, 1))] != tar) {
						out = 1;
						break;
					}
					eq[uf.find(en(i, 1))] = tar;
					ika[uf.find(en(i, 0))] = min(ika[uf.find(en(i, 0))], u[i] % 10);
				} else {
					int i = twos[I];
					ika[uf.find(en(i, 1))] = min(ika[uf.find(en(i, 1))], u[i] / 10 - 1);
				}
			}
			if(out) continue;
			rep(i, m) {
				if(!(bit >> i & 1)) {
					if(u[i] < 10) {
						ika[uf.find(en(i, 0))] = min(ika[uf.find(en(i, 0))], u[i] % 10);
					}
				}
			}
			vector<int> flag(30);
			for(auto &x : vars)
				if(x & 1) flag[uf.find(x)] = 1;
			ll ans = 1;
			set<int> vis;
			for(auto &x : vars) {
				int root = uf.find(x);
				if(vis.count(root)) continue;
				vis.insert(root);
				if(eq[root] != -1) {
					if(ika[root] < eq[root] or (flag[root] and !eq[root])) {
						out = 1;
						break;
					}
				} else {
					ans *= ika[root] + !flag[root];
					ans %= mod;
				}
			}
			if(out) continue;
			res += ans;
			res %= mod;
		}
	}
	cout << res << endl;
}
accuracy: 1
time_ms: 10
memory_kb: 3468
final_rank: 2

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ll = long long;
#define rep(i, s, t) for (int i = (int)(s); i < (int)(t); ++i)
#define revrep(i, t, s) for (int i = (int)(t)-1; i >= (int)(s); --i)
#define all(x) begin(x), end(x)
template <typename T> bool chmax(T& a, const T& b) { return a < b ? (a = b, 1) : 0; }
template <typename T> bool chmin(T& a, const T& b) { return a > b ? (a = b, 1) : 0; }

template <typename T>
ostream& operator<<(ostream& os, const vector<T>& v) {
    for (int i = 0; i < (int) v.size(); ++i) {
        os << v[i];
        if (i < (int) v.size() - 1) os << " ";
    }
    return os;
}

class UnionFind {
public:
    UnionFind() = default;
    explicit UnionFind(int n) : data(n, -1) {}

    int find(int x) {
        if (data[x] < 0) return x;
        return data[x] = find(data[x]);
    }

    void unite(int x, int y) {
        x = find(x);
        y = find(y);
        if (x == y) return;
        if (data[x] > data[y]) std::swap(x, y);
        data[x] += data[y];
        data[y] = x;
    }

    bool same(int x, int y) {
        return find(x) == find(y);
    }

    int size(int x) {
        return -data[find(x)];
    }

private:
    std::vector<int> data;
};

template <int mod>
class Modint {
    using mint = Modint;
    static_assert(mod > 0, "Modulus must be positive");

public:
    static constexpr int get_mod() noexcept { return mod; }

    constexpr Modint(long long y = 0) noexcept : x(y >= 0 ? y % mod : (y % mod + mod) % mod) {}

    constexpr int value() const noexcept { return x; }

    constexpr mint& operator+=(const mint& r) noexcept { if ((x += r.x) >= mod) x -= mod; return *this; }
    constexpr mint& operator-=(const mint& r) noexcept { if ((x += mod - r.x) >= mod) x -= mod; return *this; }
    constexpr mint& operator*=(const mint& r) noexcept { x = static_cast<int>(1LL * x * r.x % mod); return *this; }
    constexpr mint& operator/=(const mint& r) noexcept { *this *= r.inv(); return *this; }

    constexpr mint operator-() const noexcept { return mint(-x); }

    constexpr mint operator+(const mint& r) const noexcept { return mint(*this) += r; }
    constexpr mint operator-(const mint& r) const noexcept { return mint(*this) -= r; }
    constexpr mint operator*(const mint& r) const noexcept { return mint(*this) *= r; }
    constexpr mint operator/(const mint& r) const noexcept { return mint(*this) /= r; }

    constexpr bool operator==(const mint& r) const noexcept { return x == r.x; }
    constexpr bool operator!=(const mint& r) const noexcept { return x != r.x; }

    constexpr mint inv() const noexcept {
        int a = x, b = mod, u = 1, v = 0;
        while (b > 0) {
            int t = a / b;
            std::swap(a -= t * b, b);
            std::swap(u -= t * v, v);
        }
        return mint(u);
    }

    constexpr mint pow(long long n) const noexcept {
        mint ret(1), mul(x);
        while (n > 0) {
            if (n & 1) ret *= mul;
            mul *= mul;
            n >>= 1;
        }
        return ret;
    }

    friend std::ostream& operator<<(std::ostream& os, const mint& r) {
        return os << r.x;
    }

    friend std::istream& operator>>(std::istream& is, mint& r) {
        long long t;
        is >> t;
        r = mint(t);
        return is;
    }

private:
    int x;
};

using mint = Modint<(int) 1e9+7>;

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    cout << fixed << setprecision(15);

    int N, m;
    cin >> N >> m;
    string s;
    cin >> s;
    vector<char> var(m);
    vector<int> u(m);
    rep(i,0,m) {
        cin >> var[i] >> u[i];
    }
    mint ans = 0;
    rep(S,0,1<<m) {
        // fix the number of digits
        string t = "";
        rep(i,0,N) {
            if ('0' <= s[i] && s[i] <= '9') {
                t.push_back(s[i]);
            } else {
                rep(j,0,m) {
                    if (s[i] == var[j]) {
                        if (S>>j&1) {
                            t.push_back(s[i]-'a'+'A');
                            t.push_back(s[i]);
                        } else {
                            t.push_back(s[i]);
                        }
                    }
                }
            }
        }
        // connect same digits
        UnionFind uf(256);
        rep(i,0,t.size()/2) {
            uf.unite(t[i], t[t.size()-1-i]);
        }
        // check if two different numbers are connected
        bool ok = true;
        rep(i,'0','9'+1) {
            rep(j,i+1,'9'+1) {
                if (uf.same(i, j)) {
                    ok = false;
                    break;
                }
            }
            if (!ok) break;
        }
        if (!ok) continue;
        // get the number for each component
        vector<int> num(256, -1);
        rep(i,'0','9'+1) {
            num[uf.find(i)] = i - '0';
        }
        // fix which letter has the maximum top digit
        int T = S;
        do {
            vector<int> lb(256, 0), ub(256, 9);
            rep(i,'0','9'+1) lb[uf.find(i)] = ub[uf.find(i)] = i - '0';
            bool ok = true;
            rep(i,0,m) {
                if (S>>i&1) {
                    int x1 = uf.find(var[i]-'a'+'A');
                    int x2 = uf.find(var[i]);

                    if (ub[x1] < 1) {
                        ok = false;
                        break;
                    }
                    chmax(lb[x1], 1);

                    if (T>>i&1) {
                        int y = u[i]/10;
                        if (y < lb[x1] || ub[x1] < y) {
                            ok = false;
                            break;
                        }
                        lb[x1] = ub[x1] = y;

                        y = u[i] % 10;
                        if (y < lb[x2]) {
                            ok = false;
                            break;
                        }
                        chmin(ub[x2], y);
                    } else {
                        int y = u[i]/10 - 1;
                        if (y < lb[x1]) {
                            ok = false;
                            break;
                        }
                        chmin(ub[x1], y);
                    }
                } else {
                    int x = uf.find(var[i]);
                    int y = min(9, u[i]);
                    if (y < lb[x]) {
                        ok = false;
                        break;
                    }
                    chmin(ub[x], y);
                }
            }
            if (ok) {
                mint res = 1;
                set<int> st;
                for (char i : t) {
                    if (i == uf.find(i) && !st.count(i)) {
                        if (lb[i] > ub[i]) {
                            res = 0;
                            break;
                        }
                        res *= ub[i] - lb[i] + 1;
                        st.insert(i);
                    }
                }
                ans += res;
            }
            T = (T - 1) & S;
        } while (T != S);
    }
    cout << ans << endl;
}
accuracy: 1
time_ms: 20
memory_kb: 3504
final_rank: 6

code_snippet: #include <bits/stdc++.h>

#ifdef _MSC_VER
#  include <intrin.h>
#else
#  include <x86intrin.h>
#endif

#include <limits>
#include <type_traits>

namespace suisen {
// ! utility
template <typename ...Types>
using constraints_t = std::enable_if_t<std::conjunction_v<Types...>, std::nullptr_t>;
template <bool cond_v, typename Then, typename OrElse>
constexpr decltype(auto) constexpr_if(Then&& then, OrElse&& or_else) {
    if constexpr (cond_v) {
        return std::forward<Then>(then);
    } else {
        return std::forward<OrElse>(or_else);
    }
}

// ! function
template <typename ReturnType, typename Callable, typename ...Args>
using is_same_as_invoke_result = std::is_same<std::invoke_result_t<Callable, Args...>, ReturnType>;
template <typename F, typename T>
using is_uni_op = is_same_as_invoke_result<T, F, T>;
template <typename F, typename T>
using is_bin_op = is_same_as_invoke_result<T, F, T, T>;

template <typename Comparator, typename T>
using is_comparator = std::is_same<std::invoke_result_t<Comparator, T, T>, bool>;

// ! integral
template <typename T, typename = constraints_t<std::is_integral<T>>>
constexpr int bit_num = std::numeric_limits<std::make_unsigned_t<T>>::digits;
template <typename T, unsigned int n>
struct is_nbit { static constexpr bool value = bit_num<T> == n; };
template <typename T, unsigned int n>
static constexpr bool is_nbit_v = is_nbit<T, n>::value;

// ?
template <typename T>
struct safely_multipliable {};
template <>
struct safely_multipliable<int> { using type = long long; };
template <>
struct safely_multipliable<long long> { using type = __int128_t; };
template <>
struct safely_multipliable<unsigned int> { using type = unsigned long long; };
template <>
struct safely_multipliable<unsigned long int> { using type = __uint128_t; };
template <>
struct safely_multipliable<unsigned long long> { using type = __uint128_t; };
template <>
struct safely_multipliable<float> { using type = float; };
template <>
struct safely_multipliable<double> { using type = double; };
template <>
struct safely_multipliable<long double> { using type = long double; };
template <typename T>
using safely_multipliable_t = typename safely_multipliable<T>::type;

template <typename T, typename = void>
struct rec_value_type {
    using type = T;
};
template <typename T>
struct rec_value_type<T, std::void_t<typename T::value_type>> {
    using type = typename rec_value_type<typename T::value_type>::type;
};
template <typename T>
using rec_value_type_t = typename rec_value_type<T>::type;

} // namespace suisen

// ! type aliases
using i128 = __int128_t;
using u128 = __uint128_t;

template <typename T>
using pq_greater = std::priority_queue<T, std::vector<T>, std::greater<T>>;
template <typename T, typename U>
using umap = std::unordered_map<T, U>;

// ! macros (capital: internal macro)
#define OVERLOAD2(_1,_2,name,...) name
#define OVERLOAD3(_1,_2,_3,name,...) name
#define OVERLOAD4(_1,_2,_3,_4,name,...) name

#define REP4(i,l,r,s)  for(std::remove_reference_t<std::remove_const_t<decltype(r)>>i=(l);i<(r);i+=(s))
#define REP3(i,l,r)    REP4(i,l,r,1)
#define REP2(i,n)      REP3(i,0,n)
#define REPINF3(i,l,s) for(std::remove_reference_t<std::remove_const_t<decltype(l)>>i=(l);;i+=(s))
#define REPINF2(i,l)   REPINF3(i,l,1)
#define REPINF1(i)     REPINF2(i,0)
#define RREP4(i,l,r,s) for(std::remove_reference_t<std::remove_const_t<decltype(r)>>i=(l)+fld((r)-(l)-1,s)*(s);i>=(l);i-=(s))
#define RREP3(i,l,r)   RREP4(i,l,r,1)
#define RREP2(i,n)     RREP3(i,0,n)

#define rep(...)    OVERLOAD4(__VA_ARGS__, REP4   , REP3   , REP2   )(__VA_ARGS__)
#define rrep(...)   OVERLOAD4(__VA_ARGS__, RREP4  , RREP3  , RREP2  )(__VA_ARGS__)
#define repinf(...) OVERLOAD3(__VA_ARGS__, REPINF3, REPINF2, REPINF1)(__VA_ARGS__)

#define CAT_I(a, b) a##b
#define CAT(a, b) CAT_I(a, b)
#define UNIQVAR(tag) CAT(tag, __LINE__)
#define loop(n) for (std::remove_reference_t<std::remove_const_t<decltype(n)>> UNIQVAR(loop_variable) = n; UNIQVAR(loop_variable) --> 0;)

#define all(iterable) std::begin(iterable), std::end(iterable)
#define input(type, ...) type __VA_ARGS__; read(__VA_ARGS__)

#ifdef LOCAL
#  define debug(...) debug_internal(#__VA_ARGS__, __VA_ARGS__)

template <class T, class... Args>
void debug_internal(const char* s, T&& first, Args&&... args) {
    constexpr const char* prefix = "[\033[32mDEBUG\033[m] ";
    constexpr const char* open_brakets = sizeof...(args) == 0 ? "" : "(";
    constexpr const char* close_brakets = sizeof...(args) == 0 ? "" : ")";
    std::cerr << prefix << open_brakets << s << close_brakets << ": " << open_brakets << std::forward<T>(first);
    ((std::cerr << ", " << std::forward<Args>(args)), ...);
    std::cerr << close_brakets << "\n";
}

#else
#  define debug(...) void(0)
#endif

// ! I/O utilities

// __int128_t
std::ostream& operator<<(std::ostream& dest, __int128_t value) {
    std::ostream::sentry s(dest);
    if (s) {
        __uint128_t tmp = value < 0 ? -value : value;
        char buffer[128];
        char* d = std::end(buffer);
        do {
            --d;
            *d = "0123456789"[tmp % 10];
            tmp /= 10;
        } while (tmp != 0);
        if (value < 0) {
            --d;
            *d = '-';
        }
        int len = std::end(buffer) - d;
        if (dest.rdbuf()->sputn(d, len) != len) {
            dest.setstate(std::ios_base::badbit);
        }
    }
    return dest;
}
// __uint128_t
std::ostream& operator<<(std::ostream& dest, __uint128_t value) {
    std::ostream::sentry s(dest);
    if (s) {
        char buffer[128];
        char* d = std::end(buffer);
        do {
            --d;
            *d = "0123456789"[value % 10];
            value /= 10;
        } while (value != 0);
        int len = std::end(buffer) - d;
        if (dest.rdbuf()->sputn(d, len) != len) {
            dest.setstate(std::ios_base::badbit);
        }
    }
    return dest;
}

// pair
template <typename T, typename U>
std::ostream& operator<<(std::ostream& out, const std::pair<T, U>& a) {
    return out << a.first << ' ' << a.second;
}
// tuple
template <unsigned int N = 0, typename ...Args>
std::ostream& operator<<(std::ostream& out, const std::tuple<Args...>& a) {
    if constexpr (N >= std::tuple_size_v<std::tuple<Args...>>) {
        return out;
    } else {
        out << std::get<N>(a);
        if constexpr (N + 1 < std::tuple_size_v<std::tuple<Args...>>) {
            out << ' ';
        }
        return operator<<<N + 1>(out, a);
    }
}
// vector
template <typename T>
std::ostream& operator<<(std::ostream& out, const std::vector<T>& a) {
    for (auto it = a.begin(); it != a.end();) {
        out << *it;
        if (++it != a.end()) out << ' ';
    }
    return out;
}
// array
template <typename T, size_t N>
std::ostream& operator<<(std::ostream& out, const std::array<T, N>& a) {
    for (auto it = a.begin(); it != a.end();) {
        out << *it;
        if (++it != a.end()) out << ' ';
    }
    return out;
}
inline void print() { std::cout << '\n'; }
template <typename Head, typename... Tail>
inline void print(const Head& head, const Tail &...tails) {
    std::cout << head;
    if (sizeof...(tails)) std::cout << ' ';
    print(tails...);
}
template <typename Iterable>
auto print_all(const Iterable& v, std::string sep = " ", std::string end = "\n") -> decltype(std::cout << *v.begin(), void()) {
    for (auto it = v.begin(); it != v.end();) {
        std::cout << *it;
        if (++it != v.end()) std::cout << sep;
    }
    std::cout << end;
}

__int128_t parse_i128(std::string& s) {
    __int128_t ret = 0;
    for (int i = 0; i < int(s.size()); i++) if ('0' <= s[i] and s[i] <= '9') ret = 10 * ret + s[i] - '0';
    if (s[0] == '-') ret = -ret;
    return ret;
}
__uint128_t parse_u128(std::string& s) {
    __uint128_t ret = 0;
    for (int i = 0; i < int(s.size()); i++) if ('0' <= s[i] and s[i] <= '9') ret = 10 * ret + s[i] - '0';
    return ret;
}
// __int128_t
std::istream& operator>>(std::istream& in, __int128_t& v) {
    std::string s;
    in >> s;
    v = parse_i128(s);
    return in;
}
// __uint128_t
std::istream& operator>>(std::istream& in, __uint128_t& v) {
    std::string s;
    in >> s;
    v = parse_u128(s);
    return in;
}
// pair
template <typename T, typename U>
std::istream& operator>>(std::istream& in, std::pair<T, U>& a) {
    return in >> a.first >> a.second;
}
// tuple
template <unsigned int N = 0, typename ...Args>
std::istream& operator>>(std::istream& in, std::tuple<Args...>& a) {
    if constexpr (N >= std::tuple_size_v<std::tuple<Args...>>) {
        return in;
    } else {
        return operator>><N + 1>(in >> std::get<N>(a), a);
    }
}
// vector
template <typename T>
std::istream& operator>>(std::istream& in, std::vector<T>& a) {
    for (auto it = a.begin(); it != a.end(); ++it) in >> *it;
    return in;
}
// array
template <typename T, size_t N>
std::istream& operator>>(std::istream& in, std::array<T, N>& a) {
    for (auto it = a.begin(); it != a.end(); ++it) in >> *it;
    return in;
}
template <typename ...Args>
void read(Args &...args) {
    (std::cin >> ... >> args);
}

// ! integral utilities

// Returns pow(-1, n)
template <typename T>
constexpr inline int pow_m1(T n) {
    return -(n & 1) | 1;
}
// Returns pow(-1, n)
template <>
constexpr inline int pow_m1<bool>(bool n) {
    return -int(n) | 1;
}

// Returns floor(x / y)
template <typename T>
constexpr inline T fld(const T x, const T y) {
    return (x ^ y) >= 0 ? x / y : (x - (y + pow_m1(y >= 0))) / y;
}
template <typename T>
constexpr inline T cld(const T x, const T y) {
    return (x ^ y) <= 0 ? x / y : (x + (y + pow_m1(y >= 0))) / y;
}

template <typename T, suisen::constraints_t<suisen::is_nbit<T, 16>> = nullptr>
__attribute__((target("popcnt"))) constexpr inline int popcount(const T x) { return _mm_popcnt_u32(x); }
template <typename T, suisen::constraints_t<suisen::is_nbit<T, 32>> = nullptr>
__attribute__((target("popcnt"))) constexpr inline int popcount(const T x) { return _mm_popcnt_u32(x); }
template <typename T, suisen::constraints_t<suisen::is_nbit<T, 64>> = nullptr>
__attribute__((target("popcnt"))) constexpr inline int popcount(const T x) { return _mm_popcnt_u64(x); }
template <typename T, suisen::constraints_t<suisen::is_nbit<T, 16>> = nullptr>
constexpr inline int count_lz(const T x) { return x ? __builtin_clz(x) : suisen::bit_num<T>; }
template <typename T, suisen::constraints_t<suisen::is_nbit<T, 32>> = nullptr>
constexpr inline int count_lz(const T x) { return x ? __builtin_clz(x) : suisen::bit_num<T>; }
template <typename T, suisen::constraints_t<suisen::is_nbit<T, 64>> = nullptr>
constexpr inline int count_lz(const T x) { return x ? __builtin_clzll(x) : suisen::bit_num<T>; }
template <typename T, suisen::constraints_t<suisen::is_nbit<T, 16>> = nullptr>
constexpr inline int count_tz(const T x) { return x ? __builtin_ctz(x) : suisen::bit_num<T>; }
template <typename T, suisen::constraints_t<suisen::is_nbit<T, 32>> = nullptr>
constexpr inline int count_tz(const T x) { return x ? __builtin_ctz(x) : suisen::bit_num<T>; }
template <typename T, suisen::constraints_t<suisen::is_nbit<T, 64>> = nullptr>
constexpr inline int count_tz(const T x) { return x ? __builtin_ctzll(x) : suisen::bit_num<T>; }
template <typename T>
constexpr inline int floor_log2(const T x) { return suisen::bit_num<T> -1 - count_lz(x); }
template <typename T>
constexpr inline int ceil_log2(const T x) { return floor_log2(x) + ((x & -x) != x); }
template <typename T>
constexpr inline int kth_bit(const T x, const unsigned int k) { return (x >> k) & 1; }
template <typename T>
constexpr inline int parity(const T x) { return popcount(x) & 1; }

// ! container

template <typename T, typename Comparator, suisen::constraints_t<suisen::is_comparator<Comparator, T>> = nullptr>
auto priqueue_comp(const Comparator comparator) {
    return std::priority_queue<T, std::vector<T>, Comparator>(comparator);
}

template <typename Iterable>
auto isize(const Iterable& iterable) -> decltype(int(iterable.size())) {
    return iterable.size();
}

template <typename T, typename Gen, suisen::constraints_t<suisen::is_same_as_invoke_result<T, Gen, int>> = nullptr>
auto generate_vector(int n, Gen generator) {
    std::vector<T> v(n);
    for (int i = 0; i < n; ++i) v[i] = generator(i);
    return v;
}
template <typename T>
auto generate_range_vector(T l, T r) {
    return generate_vector(r - l, [l](int i) { return l + i; });
}
template <typename T>
auto generate_range_vector(T n) {
    return generate_range_vector(0, n);
}

template <typename T>
void sort_unique_erase(std::vector<T>& a) {
    std::sort(a.begin(), a.end());
    a.erase(std::unique(a.begin(), a.end()), a.end());
}

template <typename InputIterator, typename BiConsumer>
auto foreach_adjacent_values(InputIterator first, InputIterator last, BiConsumer f) -> decltype(f(*first++, *last), void()) {
    if (first != last) for (auto itr = first, itl = itr++; itr != last; itl = itr++) f(*itl, *itr);
}
template <typename Container, typename BiConsumer>
auto foreach_adjacent_values(Container c, BiConsumer f) -> decltype(c.begin(), c.end(), void()) {
    foreach_adjacent_values(c.begin(), c.end(), f);
}

// ! other utilities

// x <- min(x, y). returns true iff `x` has chenged.
template <typename T>
inline bool chmin(T& x, const T& y) {
    if (y >= x) return false;
    x = y;
    return true;
}
// x <- max(x, y). returns true iff `x` has chenged.
template <typename T>
inline bool chmax(T& x, const T& y) {
    if (y <= x) return false;
    x = y;
    return true;
}

template <typename T, std::enable_if_t<std::is_integral_v<T>, std::nullptr_t> = nullptr>
std::string bin(T val, int bit_num = -1) {
    std::string res;
    if (bit_num >= 0) {
        for (int bit = bit_num; bit-- > 0;) res += '0' + ((val >> bit) & 1);
    } else {
        for (; val; val >>= 1) res += '0' + (val & 1);
        std::reverse(res.begin(), res.end());
    }
    return res;
}

template <typename T, std::enable_if_t<std::is_integral_v<T>, std::nullptr_t> = nullptr>
std::vector<T> digits_low_to_high(T val, T base = 10) {
    std::vector<T> res;
    for (; val; val /= base) res.push_back(val % base);
    if (res.empty()) res.push_back(T{ 0 });
    return res;
}
template <typename T, std::enable_if_t<std::is_integral_v<T>, std::nullptr_t> = nullptr>
std::vector<T> digits_high_to_low(T val, T base = 10) {
    auto res = digits_low_to_high(val, base);
    std::reverse(res.begin(), res.end());
    return res;
}

template <typename T>
std::string join(const std::vector<T>& v, const std::string& sep, const std::string& end) {
    std::ostringstream ss;
    for (auto it = v.begin(); it != v.end();) {
        ss << *it;
        if (++it != v.end()) ss << sep;
    }
    ss << end;
    return ss.str();
}

namespace suisen {}
using namespace suisen;
using namespace std;

struct io_setup {
    io_setup(int precision = 20) {
        std::ios::sync_with_stdio(false);
        std::cin.tie(nullptr);
        std::cout << std::fixed << std::setprecision(precision);
    }
} io_setup_{};

// ! code from here

#include <algorithm>
#include <cassert>
#include <vector>

namespace atcoder {

// Implement (union by size) + (path compression)
// Reference:
// Zvi Galil and Giuseppe F. Italiano,
// Data structures and algorithms for disjoint set union problems
struct dsu {
  public:
    dsu() : _n(0) {}
    explicit dsu(int n) : _n(n), parent_or_size(n, -1) {}

    int merge(int a, int b) {
        assert(0 <= a && a < _n);
        assert(0 <= b && b < _n);
        int x = leader(a), y = leader(b);
        if (x == y) return x;
        if (-parent_or_size[x] < -parent_or_size[y]) std::swap(x, y);
        parent_or_size[x] += parent_or_size[y];
        parent_or_size[y] = x;
        return x;
    }

    bool same(int a, int b) {
        assert(0 <= a && a < _n);
        assert(0 <= b && b < _n);
        return leader(a) == leader(b);
    }

    int leader(int a) {
        assert(0 <= a && a < _n);
        if (parent_or_size[a] < 0) return a;
        return parent_or_size[a] = leader(parent_or_size[a]);
    }

    int size(int a) {
        assert(0 <= a && a < _n);
        return -parent_or_size[leader(a)];
    }

    std::vector<std::vector<int>> groups() {
        std::vector<int> leader_buf(_n), group_size(_n);
        for (int i = 0; i < _n; i++) {
            leader_buf[i] = leader(i);
            group_size[leader_buf[i]]++;
        }
        std::vector<std::vector<int>> result(_n);
        for (int i = 0; i < _n; i++) {
            result[i].reserve(group_size[i]);
        }
        for (int i = 0; i < _n; i++) {
            result[leader_buf[i]].push_back(i);
        }
        result.erase(
            std::remove_if(result.begin(), result.end(),
                           [&](const std::vector<int>& v) { return v.empty(); }),
            result.end());
        return result;
    }

  private:
    int _n;
    // root node: -1 * component size
    // otherwise: parent
    std::vector<int> parent_or_size;
};

}  // namespace atcoder

#ifdef _MSC_VER
#  include <intrin.h>
#else
#  include <x86intrin.h>
#endif

#include <cstdint>
#include <iostream>

namespace suisen {
    template <typename T, std::enable_if_t<std::is_integral_v<T>, std::nullptr_t> = nullptr>
    struct all_subset {
        struct all_subset_iter {
            const T s; T t;
            constexpr all_subset_iter(T s) : s(s), t(s + 1) {}
            constexpr auto operator*() const { return t; }
            constexpr auto operator++() {}
            constexpr auto operator!=(std::nullptr_t) { return t ? (--t &= s, true) : false; }
        };
        T s;
        constexpr all_subset(T s) : s(s) {}
        constexpr auto begin() { return all_subset_iter(s); }
        constexpr auto end() { return nullptr; }
    };

    // iterator over T s.t. T is subset of S and |T| = k
    struct all_subset_k {
        struct all_subset_k_iter {
            const uint32_t n, k, s;
            uint32_t t;
            __attribute__((target("avx2")))
            all_subset_k_iter(uint32_t s, uint32_t k) : n(uint32_t(1) << _mm_popcnt_u32(s)), k(k), s(s), t((uint32_t(1) << k) - 1) {}
            __attribute__((target("bmi2")))
            auto operator*() const { return _pdep_u32(t, s); }
            __attribute__((target("bmi")))
            auto operator++() {
                if (k == 0) {
                    t = std::numeric_limits<uint32_t>::max();
                } else {
                    uint32_t y = t + _blsi_u32(t); // t + (-t & t)
                    t = y | ((y ^ t) >> _tzcnt_u32(t << 2));
                }
            }
            auto operator!=(std::nullptr_t) const { return t < n; }
        };
        uint32_t s, k;
        all_subset_k(uint32_t s, uint32_t k) : s(s), k(k) {
            assert(s != std::numeric_limits<uint32_t>::max());
        }
        auto begin() { return all_subset_k_iter(s, k); }
        auto end() { return nullptr; }
    };

    struct all_subset_k_64 {
        struct all_subset_k_iter_64 {
            const uint64_t n, s;
            const uint32_t k;
            uint64_t t;
            __attribute__((target("avx2")))
            all_subset_k_iter_64(uint64_t s, uint32_t k) : n(uint64_t(1) << _mm_popcnt_u64(s)), s(s), k(k), t((uint64_t(1) << k) - 1) {}
            __attribute__((target("bmi2")))
            auto operator*() const { return _pdep_u64(t, s); }
            __attribute__((target("bmi")))
            auto operator++() {
                if (k == 0) {
                    t = std::numeric_limits<uint64_t>::max();
                } else {
                    uint64_t y = t + _blsi_u64(t);
                    t = y | ((y ^ t) >> _tzcnt_u64(t << 2));
                }
            }
            auto operator!=(std::nullptr_t) const { return t < n; }
        };
        uint64_t s;
        uint32_t k;
        all_subset_k_64(uint64_t s, uint32_t k) : s(s), k(k) {
            assert(s != std::numeric_limits<uint64_t>::max());
        }
        auto begin() { return all_subset_k_iter_64(s, k); }
        auto end() { return nullptr; }
    };

    struct all_setbit {
        struct all_setbit_iter {
            uint32_t s;
            all_setbit_iter(uint32_t s) : s(s) {}
            __attribute__((target("bmi")))
            auto operator*() { return _tzcnt_u32(s); }
            __attribute__((target("bmi")))
            auto operator++() { s = __blsr_u32(s); }
            auto operator!=(std::nullptr_t) { return s; }
        };
        uint32_t s;
        all_setbit(uint32_t s) : s(s) {}
        auto begin() { return all_setbit_iter(s); }
        auto end() { return nullptr; }
    };

    struct all_setbit_64 {
        struct all_setbit_iter_64 {
            uint64_t s;
            all_setbit_iter_64(uint64_t s) : s(s) {}
            __attribute__((target("bmi")))
            auto operator*() { return _tzcnt_u64(s); }
            __attribute__((target("bmi")))
            auto operator++() { s = __blsr_u64(s); }
            auto operator!=(std::nullptr_t) { return s; }
        };
        uint64_t s;
        all_setbit_64(uint64_t s) : s(s) {}
        auto begin() { return all_setbit_iter_64(s); }
        auto end() { return nullptr; }
    };
} // namespace suisen

constexpr int MOD = 1'000'000'007;

int main() {
    input(int, n, m);
    input(string, s);

    vector<pair<char, array<int, 2>>> vars(m);
    array<char, 256> ch_to_id{};
    rep(i, m) {
        input(char, c);
        input(int, u);
        vars[i] = { c, { u % 10, u / 10 } };
        ch_to_id[c] = i;
    }

    long long ans = 0;
    rep(t, 1 << m) {
        int next_id = 0;
        vector<vector<int>> ids(m);
        vector<pair<int, int>> id_inv;
        rep(i, m) {
            if (kth_bit(t, i)) {
                id_inv.emplace_back(i, 1);
                ids[i].push_back(next_id++);
            }
            id_inv.emplace_back(i, 0);
            ids[i].push_back(next_id++);
        }

        vector<int> s2;
        for (char c : s) {
            if ('0' <= c and c <= '9') {
                s2.push_back(next_id + (c - '0'));
            } else {
                const int i = ch_to_id[c];
                for (int id : ids[i]) {
                    s2.push_back(id);
                }
            }
        }
        const int len = s2.size();

        for (int u : all_subset(t)) {
            atcoder::dsu uf(next_id + 10);
            rep(idx, len) {
                uf.merge(s2[idx], s2[len - 1 - idx]);
            }
            bool valid = true;
            for (int id : s2) if (id < next_id) {
                auto [i, dig] = id_inv[id];
                if (dig == 1 and kth_bit(u, i)) {
                    auto [u0, u1] = vars[i].second;
                    uf.merge(id, next_id + u1);
                    valid &= u1 != 0;
                }
            }
            if (not valid) continue;

            const auto groups = uf.groups();

            vector<int> cmp(next_id + 10, -1);
            int c = 0;
            for (auto group : groups) {
                for (int id : group) cmp[id] = c;
                ++c;
            }

            vector<int> lo(c, 0), hi(c, 9);
            for (auto group : groups) {
                const int cid = cmp[group[0]];
                int cval = -1;
                for (int id : group) {
                    if (id >= next_id) {
                        if (cval < 0) {
                            cval = id - next_id;
                        } else {
                            valid = false;
                            break;
                        }
                    } else {
                        auto [i, dig] = id_inv[id];
                        auto [u0, u1] = vars[i].second;
                        if (dig == 1) {
                            chmax(lo[cid], 1);
                            if (kth_bit(u, i)) {
                                chmin(hi[cid], u1);
                                chmax(lo[cid], u1);
                                const int cid2 = cmp[ids[i][1]];
                                chmin(hi[cid2], u0);
                            } else {
                                chmin(hi[cid], u1 - 1);
                            }
                        } else if (u1 == 0) {
                            chmin(hi[cid], u0);
                        }
                    }
                }
                if (cval >= 0) {
                    chmax(lo[cid], cval);
                    chmin(hi[cid], cval);
                }
                if (not valid) break;
            }
            if (not valid) continue;

            long long res = 1;
            rep(i, c) {
                res *= max(hi[i] - lo[i] + 1, 0);
                res %= MOD;
            }
            ans += res;
            ans %= MOD;
        }
    }
    print(ans);
    return 0;
}
accuracy: 1
time_ms: 390
memory_kb: 3472
final_rank: 11

code_snippet: #include <bits/stdc++.h>

using namespace std;

#define eps 1e-9
#define INF 2000000000              // 2e9
#define LLINF 2000000000000000000ll // 2e18 (llmax:9e18)
#define all(x) (x).begin(), (x).end()
#define sq(x) ((x) * (x))

#define rep(i, x) for (int i = 0; i < (int)(x); i++)
#define drep(i, x) for (int i = ((int)(x)-1); i >= 0; i--)

#define popcount __builtin_popcount
#define next __next
#define prev __prev

#ifndef LOCAL
#define dmp(...) ;
#else
#define dmp(...)                                                               \
  cerr << "[ " << #__VA_ARGS__ << " ] : " << dump_str(__VA_ARGS__) << endl
#endif

// ---------------- Utility ------------------

template <class T>
bool chmin(T &a, const T &b) {
  if (a <= b) return false;
  a = b;
  return true;
}

template <class T>
bool chmax(T &a, const T &b) {
  if (a >= b) return false;
  a = b;
  return true;
}

template <class T>
using MaxHeap = priority_queue<T>;

template <class T>
using MinHeap = priority_queue<T, vector<T>, greater<T>>;

template <class T>
vector<T> vect(int len, T elem) {
  return vector<T>(len, elem);
}

// ----------------- Input -------------------

template <class T, class U>
istream &operator>>(istream &is, pair<T, U> &p) {
  return is >> p.first >> p.second;
}

template <class T>
istream &operator>>(istream &is, vector<T> &vec) {
  for (int i = 0; i < vec.size(); i++) is >> vec[i];
  return is;
}

// ----------------- Output ------------------

template <class T, class U>
ostream &operator<<(ostream &os, const pair<T, U> &p) {
  return os << p.first << ',' << p.second;
}

template <class T>
ostream &operator<<(ostream &os, const vector<T> &v) {
  for (const T &e : v) os << e << " ";
  return os;
}

template <class T>
ostream &operator<<(ostream &os, const deque<T> &d) {
  for (const T &e : d) os << e << " ";
  return os;
}

template <class T>
ostream &operator<<(ostream &os, const set<T> &s) {
  os << "{ ";
  for (const T &e : s) os << e << " ";
  return os << "}";
}

template <class T, class U>
ostream &operator<<(ostream &os, const map<T, U> &m) {
  os << "{ ";
  for (const auto &[key, val] : m) os << "( " << key << " -> " << val << " ) ";
  return os << "}";
}

template <class TupleTy, size_t... I>
void dump_tuple(ostream &os, const TupleTy t, std::index_sequence<I...>) {
  (..., (os << (I == 0 ? "" : ",") << std::get<I>(t)));
}

template <class... Args>
ostream &operator<<(ostream &os, const tuple<Args...> &t) {
  os << "(";
  dump_tuple(os, t, std::make_index_sequence<sizeof...(Args)>());
  return os << ")";
}

void dump_str_rec(ostringstream &) {}

template <class Head, class... Tail>
void dump_str_rec(ostringstream &oss, Head &&head, Tail &&... tail) {
  oss << ", " << head;
  dump_str_rec(oss, forward<Tail>(tail)...);
}

template <class T, class... U>
string dump_str(T &&arg, U &&... args) {
  ostringstream oss;
  oss << arg;
  dump_str_rec(oss, forward<U>(args)...);
  return oss.str();
}

// --------------- Fast I/O ------------------

void fastio() {
  cin.tie(0);
  ios::sync_with_stdio(0);
  cout << fixed << setprecision(20);
}

// ------------------ ACL --------------------


#include <cassert>
#include <numeric>
#include <type_traits>

#ifdef _MSC_VER
#include <intrin.h>
#endif


#include <utility>

#ifdef _MSC_VER
#include <intrin.h>
#endif

namespace atcoder {

namespace internal {

constexpr long long safe_mod(long long x, long long m) {
    x %= m;
    if (x < 0) x += m;
    return x;
}

struct barrett {
    unsigned int _m;
    unsigned long long im;

    explicit barrett(unsigned int m) : _m(m), im((unsigned long long)(-1) / m + 1) {}

    unsigned int umod() const { return _m; }

    unsigned int mul(unsigned int a, unsigned int b) const {

        unsigned long long z = a;
        z *= b;
#ifdef _MSC_VER
        unsigned long long x;
        _umul128(z, im, &x);
#else
        unsigned long long x =
            (unsigned long long)(((unsigned __int128)(z)*im) >> 64);
#endif
        unsigned int v = (unsigned int)(z - x * _m);
        if (_m <= v) v += _m;
        return v;
    }
};

constexpr long long pow_mod_constexpr(long long x, long long n, int m) {
    if (m == 1) return 0;
    unsigned int _m = (unsigned int)(m);
    unsigned long long r = 1;
    unsigned long long y = safe_mod(x, m);
    while (n) {
        if (n & 1) r = (r * y) % _m;
        y = (y * y) % _m;
        n >>= 1;
    }
    return r;
}

constexpr bool is_prime_constexpr(int n) {
    if (n <= 1) return false;
    if (n == 2 || n == 7 || n == 61) return true;
    if (n % 2 == 0) return false;
    long long d = n - 1;
    while (d % 2 == 0) d /= 2;
    constexpr long long bases[3] = {2, 7, 61};
    for (long long a : bases) {
        long long t = d;
        long long y = pow_mod_constexpr(a, t, n);
        while (t != n - 1 && y != 1 && y != n - 1) {
            y = y * y % n;
            t <<= 1;
        }
        if (y != n - 1 && t % 2 == 0) {
            return false;
        }
    }
    return true;
}
template <int n> constexpr bool is_prime = is_prime_constexpr(n);

constexpr std::pair<long long, long long> inv_gcd(long long a, long long b) {
    a = safe_mod(a, b);
    if (a == 0) return {b, 0};

    long long s = b, t = a;
    long long m0 = 0, m1 = 1;

    while (t) {
        long long u = s / t;
        s -= t * u;
        m0 -= m1 * u;  // |m1 * u| <= |m1| * s <= b


        auto tmp = s;
        s = t;
        t = tmp;
        tmp = m0;
        m0 = m1;
        m1 = tmp;
    }
    if (m0 < 0) m0 += b / s;
    return {s, m0};
}

constexpr int primitive_root_constexpr(int m) {
    if (m == 2) return 1;
    if (m == 167772161) return 3;
    if (m == 469762049) return 3;
    if (m == 754974721) return 11;
    if (m == 998244353) return 3;
    int divs[20] = {};
    divs[0] = 2;
    int cnt = 1;
    int x = (m - 1) / 2;
    while (x % 2 == 0) x /= 2;
    for (int i = 3; (long long)(i)*i <= x; i += 2) {
        if (x % i == 0) {
            divs[cnt++] = i;
            while (x % i == 0) {
                x /= i;
            }
        }
    }
    if (x > 1) {
        divs[cnt++] = x;
    }
    for (int g = 2;; g++) {
        bool ok = true;
        for (int i = 0; i < cnt; i++) {
            if (pow_mod_constexpr(g, (m - 1) / divs[i], m) == 1) {
                ok = false;
                break;
            }
        }
        if (ok) return g;
    }
}
template <int m> constexpr int primitive_root = primitive_root_constexpr(m);

unsigned long long floor_sum_unsigned(unsigned long long n,
                                      unsigned long long m,
                                      unsigned long long a,
                                      unsigned long long b) {
    unsigned long long ans = 0;
    while (true) {
        if (a >= m) {
            ans += n * (n - 1) / 2 * (a / m);
            a %= m;
        }
        if (b >= m) {
            ans += n * (b / m);
            b %= m;
        }

        unsigned long long y_max = a * n + b;
        if (y_max < m) break;
        n = (unsigned long long)(y_max / m);
        b = (unsigned long long)(y_max % m);
        std::swap(m, a);
    }
    return ans;
}

}  // namespace internal

}  // namespace atcoder


#include <cassert>
#include <numeric>
#include <type_traits>

namespace atcoder {

namespace internal {

#ifndef _MSC_VER
template <class T>
using is_signed_int128 =
    typename std::conditional<std::is_same<T, __int128_t>::value ||
                                  std::is_same<T, __int128>::value,
                              std::true_type,
                              std::false_type>::type;

template <class T>
using is_unsigned_int128 =
    typename std::conditional<std::is_same<T, __uint128_t>::value ||
                                  std::is_same<T, unsigned __int128>::value,
                              std::true_type,
                              std::false_type>::type;

template <class T>
using make_unsigned_int128 =
    typename std::conditional<std::is_same<T, __int128_t>::value,
                              __uint128_t,
                              unsigned __int128>;

template <class T>
using is_integral = typename std::conditional<std::is_integral<T>::value ||
                                                  is_signed_int128<T>::value ||
                                                  is_unsigned_int128<T>::value,
                                              std::true_type,
                                              std::false_type>::type;

template <class T>
using is_signed_int = typename std::conditional<(is_integral<T>::value &&
                                                 std::is_signed<T>::value) ||
                                                    is_signed_int128<T>::value,
                                                std::true_type,
                                                std::false_type>::type;

template <class T>
using is_unsigned_int =
    typename std::conditional<(is_integral<T>::value &&
                               std::is_unsigned<T>::value) ||
                                  is_unsigned_int128<T>::value,
                              std::true_type,
                              std::false_type>::type;

template <class T>
using to_unsigned = typename std::conditional<
    is_signed_int128<T>::value,
    make_unsigned_int128<T>,
    typename std::conditional<std::is_signed<T>::value,
                              std::make_unsigned<T>,
                              std::common_type<T>>::type>::type;

#else

template <class T> using is_integral = typename std::is_integral<T>;

template <class T>
using is_signed_int =
    typename std::conditional<is_integral<T>::value && std::is_signed<T>::value,
                              std::true_type,
                              std::false_type>::type;

template <class T>
using is_unsigned_int =
    typename std::conditional<is_integral<T>::value &&
                                  std::is_unsigned<T>::value,
                              std::true_type,
                              std::false_type>::type;

template <class T>
using to_unsigned = typename std::conditional<is_signed_int<T>::value,
                                              std::make_unsigned<T>,
                                              std::common_type<T>>::type;

#endif

template <class T>
using is_signed_int_t = std::enable_if_t<is_signed_int<T>::value>;

template <class T>
using is_unsigned_int_t = std::enable_if_t<is_unsigned_int<T>::value>;

template <class T> using to_unsigned_t = typename to_unsigned<T>::type;

}  // namespace internal

}  // namespace atcoder


namespace atcoder {

namespace internal {

struct modint_base {};
struct static_modint_base : modint_base {};

template <class T> using is_modint = std::is_base_of<modint_base, T>;
template <class T> using is_modint_t = std::enable_if_t<is_modint<T>::value>;

}  // namespace internal

template <int m, std::enable_if_t<(1 <= m)>* = nullptr>
struct static_modint : internal::static_modint_base {
    using mint = static_modint;

  public:
    static constexpr int mod() { return m; }
    static mint raw(int v) {
        mint x;
        x._v = v;
        return x;
    }

    static_modint() : _v(0) {}
    template <class T, internal::is_signed_int_t<T>* = nullptr>
    static_modint(T v) {
        long long x = (long long)(v % (long long)(umod()));
        if (x < 0) x += umod();
        _v = (unsigned int)(x);
    }
    template <class T, internal::is_unsigned_int_t<T>* = nullptr>
    static_modint(T v) {
        _v = (unsigned int)(v % umod());
    }

    unsigned int val() const { return _v; }

    mint& operator++() {
        _v++;
        if (_v == umod()) _v = 0;
        return *this;
    }
    mint& operator--() {
        if (_v == 0) _v = umod();
        _v--;
        return *this;
    }
    mint operator++(int) {
        mint result = *this;
        ++*this;
        return result;
    }
    mint operator--(int) {
        mint result = *this;
        --*this;
        return result;
    }

    mint& operator+=(const mint& rhs) {
        _v += rhs._v;
        if (_v >= umod()) _v -= umod();
        return *this;
    }
    mint& operator-=(const mint& rhs) {
        _v -= rhs._v;
        if (_v >= umod()) _v += umod();
        return *this;
    }
    mint& operator*=(const mint& rhs) {
        unsigned long long z = _v;
        z *= rhs._v;
        _v = (unsigned int)(z % umod());
        return *this;
    }
    mint& operator/=(const mint& rhs) { return *this = *this * rhs.inv(); }

    mint operator+() const { return *this; }
    mint operator-() const { return mint() - *this; }

    mint pow(long long n) const {
        assert(0 <= n);
        mint x = *this, r = 1;
        while (n) {
            if (n & 1) r *= x;
            x *= x;
            n >>= 1;
        }
        return r;
    }
    mint inv() const {
        if (prime) {
            assert(_v);
            return pow(umod() - 2);
        } else {
            auto eg = internal::inv_gcd(_v, m);
            assert(eg.first == 1);
            return eg.second;
        }
    }

    friend mint operator+(const mint& lhs, const mint& rhs) {
        return mint(lhs) += rhs;
    }
    friend mint operator-(const mint& lhs, const mint& rhs) {
        return mint(lhs) -= rhs;
    }
    friend mint operator*(const mint& lhs, const mint& rhs) {
        return mint(lhs) *= rhs;
    }
    friend mint operator/(const mint& lhs, const mint& rhs) {
        return mint(lhs) /= rhs;
    }
    friend bool operator==(const mint& lhs, const mint& rhs) {
        return lhs._v == rhs._v;
    }
    friend bool operator!=(const mint& lhs, const mint& rhs) {
        return lhs._v != rhs._v;
    }

  private:
    unsigned int _v;
    static constexpr unsigned int umod() { return m; }
    static constexpr bool prime = internal::is_prime<m>;
};

template <int id> struct dynamic_modint : internal::modint_base {
    using mint = dynamic_modint;

  public:
    static int mod() { return (int)(bt.umod()); }
    static void set_mod(int m) {
        assert(1 <= m);
        bt = internal::barrett(m);
    }
    static mint raw(int v) {
        mint x;
        x._v = v;
        return x;
    }

    dynamic_modint() : _v(0) {}
    template <class T, internal::is_signed_int_t<T>* = nullptr>
    dynamic_modint(T v) {
        long long x = (long long)(v % (long long)(mod()));
        if (x < 0) x += mod();
        _v = (unsigned int)(x);
    }
    template <class T, internal::is_unsigned_int_t<T>* = nullptr>
    dynamic_modint(T v) {
        _v = (unsigned int)(v % mod());
    }

    unsigned int val() const { return _v; }

    mint& operator++() {
        _v++;
        if (_v == umod()) _v = 0;
        return *this;
    }
    mint& operator--() {
        if (_v == 0) _v = umod();
        _v--;
        return *this;
    }
    mint operator++(int) {
        mint result = *this;
        ++*this;
        return result;
    }
    mint operator--(int) {
        mint result = *this;
        --*this;
        return result;
    }

    mint& operator+=(const mint& rhs) {
        _v += rhs._v;
        if (_v >= umod()) _v -= umod();
        return *this;
    }
    mint& operator-=(const mint& rhs) {
        _v += mod() - rhs._v;
        if (_v >= umod()) _v -= umod();
        return *this;
    }
    mint& operator*=(const mint& rhs) {
        _v = bt.mul(_v, rhs._v);
        return *this;
    }
    mint& operator/=(const mint& rhs) { return *this = *this * rhs.inv(); }

    mint operator+() const { return *this; }
    mint operator-() const { return mint() - *this; }

    mint pow(long long n) const {
        assert(0 <= n);
        mint x = *this, r = 1;
        while (n) {
            if (n & 1) r *= x;
            x *= x;
            n >>= 1;
        }
        return r;
    }
    mint inv() const {
        auto eg = internal::inv_gcd(_v, mod());
        assert(eg.first == 1);
        return eg.second;
    }

    friend mint operator+(const mint& lhs, const mint& rhs) {
        return mint(lhs) += rhs;
    }
    friend mint operator-(const mint& lhs, const mint& rhs) {
        return mint(lhs) -= rhs;
    }
    friend mint operator*(const mint& lhs, const mint& rhs) {
        return mint(lhs) *= rhs;
    }
    friend mint operator/(const mint& lhs, const mint& rhs) {
        return mint(lhs) /= rhs;
    }
    friend bool operator==(const mint& lhs, const mint& rhs) {
        return lhs._v == rhs._v;
    }
    friend bool operator!=(const mint& lhs, const mint& rhs) {
        return lhs._v != rhs._v;
    }

  private:
    unsigned int _v;
    static internal::barrett bt;
    static unsigned int umod() { return bt.umod(); }
};
template <int id> internal::barrett dynamic_modint<id>::bt(998244353);

using modint998244353 = static_modint<998244353>;
using modint1000000007 = static_modint<1000000007>;
using modint = dynamic_modint<-1>;

namespace internal {

template <class T>
using is_static_modint = std::is_base_of<internal::static_modint_base, T>;

template <class T>
using is_static_modint_t = std::enable_if_t<is_static_modint<T>::value>;

template <class> struct is_dynamic_modint : public std::false_type {};
template <int id>
struct is_dynamic_modint<dynamic_modint<id>> : public std::true_type {};

template <class T>
using is_dynamic_modint_t = std::enable_if_t<is_dynamic_modint<T>::value>;

}  // namespace internal

}  // namespace atcoder

// constexpr int MOD = 998244353;
constexpr int MOD = 1000000007;
using mint = atcoder::static_modint<MOD>;
ostream &operator<<(ostream &os, const mint &v) {
  return os << v.val();
}

// ------------ End of template --------------

#define endl "\n"
using ll = long long;
using pii = pair<int, int>;

const bool multitest = false;

struct UnionFind {
  vector<int> par, rank, sz;
  UnionFind(int n) {
    par.assign(n, 0);
    rank.assign(n, 0);
    sz.assign(n, 1);
    for (int i = 0; i < n; i++) par[i] = i;
  }
  int find(const int x) { return (par[x] == x) ? x : (par[x] = find(par[x])); }
  void unite(int x, int y) {
    x = find(x);
    y = find(y);
    if (x == y) return;
    if (rank[x] < rank[y]) {
      par[x] = y;
      sz[y] += sz[x];
    } else {
      par[y] = x;
      sz[x] += sz[y];
      if (rank[x] == rank[y]) rank[x]++;
    }
  }
  bool same(const int x, const int y) { return find(x) == find(y); }
  int size(const int x) { return sz[find(x)]; }
  vector<vector<int>> components() {
    int n = par.size();
    vector<vector<int>> cs;
    vector<int> idx(n, -1);
    for (int i = 0; i < n; i++) {
      if (find(i) != i) continue;
      idx[i] = cs.size();
      cs.emplace_back();
    }
    for (int i = 0; i < n; i++) {
      int id = idx[find(i)];
      cs[id].push_back(i);
    }
    return cs;
  }
};

void solve() {
  int N, m;
  cin >> N >> m;
  string s;
  cin >> s;
  vector<char> var(m);
  vector<int> u(m);
  vector<int> rev(256);
  for (int i = 0; i < m; i++) {
    cin >> var[i] >> u[i];
    rev[var[i]] = i;
  }

  mint ans(0);

  auto sub = [&](const vector<int> &types) {
    UnionFind uf(2 * m + 10);
    // ranges for each variable
    vector<pii> rs(2 * m + 10);
    {
      // 0 ... m-1 first digit
      // m ... 2m-1 second digit
      // 2m ... 2m + 9 digit
      for (int i = 0; i < m; i++) {
        if (types[i] == 0) {
          rs[i] = pii(0, min(u[i], 9));
          rs[i + m] = pii(0, 0); // dummy
        } else if (types[i] == 1) {
          if (u[i] < 10) return;
          rs[i + m] = pii(1, u[i] / 10 - 1);
          rs[i] = pii(0, 9);
        } else {
          if (u[i] < 10) return;
          rs[i + m] = pii(u[i] / 10, u[i] / 10);
          rs[i] = pii(0, u[i] % 10);
        }
      }
      for (int i = 0; i < 10; i++) rs[2 * m + i] = pii(i, i);
    }
    // equality between variables
    {
      vector<int> id;
      for (int i = 0; i < N; i++) {
        if (isdigit(s[i])) {
          int d = s[i] - '0';
          id.push_back(2 * m + d);
        } else {
          int var_id = rev[s[i]];
          int t = types[var_id];
          if (t == 0) {
            id.push_back(var_id);
          } else if (t == 1) {
            id.push_back(var_id + m);
            id.push_back(var_id);
          } else {
            id.push_back(var_id + m);
            id.push_back(var_id);
          }
        }
      }
      for (int i = 0; i < id.size(); i++) {
        uf.unite(id[i], id[id.size() - 1 - i]);
      }
    }
    mint add(1);
    auto cmps = uf.components();
    for (const auto &cmp : cmps) {
      pii range(0, 9);
      for (int v : cmp) {
        chmax(range.first, rs[v].first);
        chmin(range.second, rs[v].second);
      }
      if (range.first > range.second) return;
      add *= mint(range.second - range.first + 1);
    }
    ans += add;
    return;
  };

  vector<int> types;
  function<void(void)> enumerate = [&]() {
    if (types.size() == m) {
      sub(types);
      return;
    }
    for (int i = 0; i < 3; i++) {
      types.push_back(i);
      enumerate();
      types.pop_back();
    }
  };
  enumerate();

  cout << ans << endl;

  return;
}

int main() {
  fastio();
  if (!multitest) {
    solve();
  } else {
    cerr << "[Warning] Multi testcase mode on" << endl;
    int t;
    cin >> t;
    while (t--) solve();
  }
  return 0;
}
accuracy: 1
time_ms: 350
memory_kb: 3480
final_rank: 10

code_snippet: #include <bits/stdc++.h>
using namespace std;

template <int mod = (int)(1e9 + 7)>
struct ModInt {
  int x;
  constexpr ModInt() : x(0) {}
  constexpr ModInt(int64_t y)
      : x(y >= 0 ? y % mod : (mod - (-y) % mod) % mod) {}
  constexpr ModInt &operator+=(const ModInt &p) noexcept {
    if ((x += p.x) >= mod) x -= mod;
    return *this;
  }
  constexpr ModInt &operator-=(const ModInt &p) noexcept {
    if ((x += mod - p.x) >= mod) x -= mod;
    return *this;
  }
  constexpr ModInt &operator*=(const ModInt &p) noexcept {
    x = (int)(1LL * x * p.x % mod);
    return *this;
  }
  constexpr ModInt &operator/=(const ModInt &p) noexcept {
    *this *= p.inverse();
    return *this;
  }
  constexpr ModInt operator-() const { return ModInt(-x); }
  constexpr ModInt operator+(const ModInt &p) const noexcept {
    return ModInt(*this) += p;
  }
  constexpr ModInt operator-(const ModInt &p) const noexcept {
    return ModInt(*this) -= p;
  }
  constexpr ModInt operator*(const ModInt &p) const noexcept {
    return ModInt(*this) *= p;
  }
  constexpr ModInt operator/(const ModInt &p) const noexcept {
    return ModInt(*this) /= p;
  }
  constexpr bool operator==(const ModInt &p) const noexcept { return x == p.x; }
  constexpr bool operator!=(const ModInt &p) const noexcept { return x != p.x; }
  constexpr ModInt inverse() const noexcept {
    int a = x, b = mod, u = 1, v = 0, t = 0;
    while (b > 0) {
      t = a / b;
      swap(a -= t * b, b);
      swap(u -= t * v, v);
    }
    return ModInt(u);
  }
  constexpr ModInt pow(int64_t n) const {
    ModInt res(1), mul(x);
    while (n) {
      if (n & 1) res *= mul;
      mul *= mul;
      n >>= 1;
    }
    return res;
  }
  friend constexpr ostream &operator<<(ostream &os, const ModInt &p) noexcept {
    return os << p.x;
  }
  friend constexpr istream &operator>>(istream &is, ModInt &a) noexcept {
    int64_t t = 0;
    is >> t;
    a = ModInt<mod>(t);
    return (is);
  }
  constexpr int get_mod() { return mod; }
};
using mint = ModInt<>;

struct Unionfind {
  // tree number
  vector<int> par;
  // constructor
  Unionfind(int n = 1) : par(n, -1) {}
  // search root
  int root(int x) {
    if (par[x] < 0) return x;
    return par[x] = root(par[x]);
  }
  // is same?
  bool issame(int x, int y) { return root(x) == root(y); }

  // add
  // already added, return 0
  bool uni(int x, int y) {
    x = root(x);
    y = root(y);
    if (x == y) return 0;
    if (par[x] > par[y]) swap(x, y);
    par[x] += par[y];
    par[y] = x;
    return 1;
  }
  int size(int x) { return -par[root(x)]; }
};
int n, m;
string s, memo;
vector<int> id, u, cha;

mint solve();

int main() {
  cin >> n >> m >> s;
  u.resize(m);
  id.resize(10);
  for (int i = 0; i < m; ++i) {
    char c;
    cin >> c >> u[i];
    id[c - 'a'] = i;
    memo += c;
  }
  cout << solve() << endl;
  return 0;
}

mint solve() {
  mint res = 0;
  for (int bdig = 0; bdig < (1 << m); ++bdig) {
    bool ch = 1;
    for (int i = 0; i < m; ++i)
      if ((bdig >> i & 1) && u[i] < 10) ch = 0;
    if (!ch) continue;
    for (int ball = 0; ball < (1 << m); ++ball) {
      array<int, 30> l{}, r{};
      unordered_map<char, int> cid;
      vector<string> v(m);
      mint now = 1;
      bool ch = 1;
      for (int i = 0; i < 10; ++i) l[i] = r[i] = cid['0' + i] = i;
      for (int i = 0; i < m; ++i) {
        int state = ((bdig >> i & 1) << 1) + (ball >> i & 1);
        cid[memo[i]] = cid.size();
        if (bdig >> i & 1) cid[memo[i] - 32] = cid.size();
        if (state == 0) {
          if (u[i] < 10) ch = 0;
          l[cid[memo[i]]] = 0, r[cid[memo[i]]] = 9;
          v[i] += memo[i];
        } else if (state == 1) {
          if (u[i] >= 10) ch = 0;
          l[cid[memo[i]]] = 0, r[cid[memo[i]]] = u[i] % 10;
          v[i] += memo[i];
        } else if (state == 2) {
          if (u[i] < 20) ch = 0;
          l[cid[memo[i]]] = 1, r[cid[memo[i]]] = u[i] / 10 - 1;
          l[cid[memo[i] - 32]] = 0, r[cid[memo[i] - 32]] = 9;
          v[i] += memo[i], v[i] += memo[i] - 32;
        } else if (state == 3) {
          l[cid[memo[i]]] = r[cid[memo[i]]] = u[i] / 10;
          l[cid[memo[i] - 32]] = 0, r[cid[memo[i] - 32]] = u[i] % 10;
          v[i] += memo[i], v[i] += memo[i] - 32;
        }
      }
      if (!ch) continue;
      Unionfind uf(cid.size());
      string t, rev;
      for (auto p : s)
        if (isdigit(p))
          t += p;
        else
          t += v[id[p - 'a']];
      int len = t.size();
      for (int i = 0; i < len; ++i) {
        int tid = uf.root(cid[t[i]]), rid = uf.root(cid[t[len - 1 - i]]);
        uf.uni(tid, rid);
        l[tid] = l[rid] = max(l[tid], l[rid]);
        r[tid] = r[rid] = min(r[tid], r[rid]);
        if (l[tid] > r[tid]) {
          ch = 0;
          break;
        }
      }
      if (!ch) continue;
      len = cid.size();
      for (int i = 0; i < len; ++i)
        if (i == uf.root(i)) now *= max(0, r[i] - l[i] + 1);
      res += now;
    }
  }
  return res;
}
accuracy: 1
time_ms: 1440
memory_kb: 3460
final_rank: 16

code_snippet: #pragma GCC ta g("avx2")
#pragma GCC optimize("Ofast")
#pragma GCC optimize("unroll-loops")
#include <bits/stdc++.h>
using namespace std;
#define DEBUG
#ifdef DEBUG
template <class T, class U>
ostream &operator<<(ostream &os, const pair<T, U> &p) {
    os << '(' << p.first << ',' << p.second << ')';
    return os;
}
template <class T> ostream &operator<<(ostream &os, const vector<T> &v) {
    os << '{';
    for(int i = 0; i < (int)v.size(); i++) {
        if(i) { os << ','; }
        os << v[i];
    }
    os << '}';
    return os;
}
void debugg() { cerr << endl; }
template <class T, class... Args>
void debugg(const T &x, const Args &... args) {
    cerr << " " << x;
    debugg(args...);
}
#define debug(...)                                                             \
    cerr << __LINE__ << " [" << #__VA_ARGS__ << "]: ", debugg(__VA_ARGS__)
#define dump(x) cerr << __LINE__ << " " << #x << " = " << (x) << endl
#else
#define debug(...) (void(0))
#define dump(x) (void(0))
#endif
using namespace std;
typedef long long ll;
typedef vector<ll> vl;
typedef vector<vl> vvl;
typedef vector<char> vc;
typedef vector<string> vs;
typedef vector<bool> vb;
typedef vector<double> vd;
typedef pair<ll,ll> P;
typedef pair<int,int> pii;
typedef vector<P> vpl;
typedef tuple<ll,ll,ll> tapu;
#define rep(i,n) for(int i=0; i<(n); i++)
#define REP(i,a,b) for(int i=(a); i<(b); i++)
#define all(x) (x).begin(), (x).end()
#define rall(x) (x).rbegin(), (x).rend()
template<typename T1,typename T2> inline bool chmin(T1 &a,T2 b){
	if(a>b){
		a = b; return true;
	}
	else return false;
}
template<typename T1,typename T2> inline bool chmax(T1 &a,T2 b){
	if(a<b){
		a = b; return true;
	}
	else return false;
}
template<typename T> inline void print(T &a){
    int sz = a.size();
    for(auto itr = a.begin(); itr != a.end(); itr++){
		cout << *itr;
        sz--;
        if(sz) cout << " ";
	}
    cout << "\n";
}
template<typename T1,typename T2> inline void print2(T1 a, T2 b){
	cout << a << " " << b << "\n";
}
template<typename T1,typename T2,typename T3> inline void print3(T1 a, T2 b, T3 c){
	cout << a << " " << b << " " << c << "\n";
}
void mark() {cout << "#" << "\n";}
ll pcount(ll x) {return __builtin_popcountll(x);}
//#include <atcoder/maxflow>
//using namespace atcoder;
const int inf = (1<<30)-1;
const ll linf = 1LL<<61;
const int MAX = 510000;
int dy[8] = {0,1,0,-1,1,-1,-1,1};
int dx[8] = {-1,0,1,0,1,-1,1,-1};
const double pi = acos(-1);
const double eps = 1e-9;
const int mod = 1e9 + 7;
//const int mod = 998244353;

template <int mod> struct ModInt {
	int x;

	ModInt() : x(0) {}

	ModInt(int64_t y) : x(y >= 0 ? y % mod : (mod - (-y) % mod) % mod) {}

	ModInt &operator+=(const ModInt &p) {
		if((x += p.x) >= mod) x -= mod;
		return *this;
	}

	ModInt &operator-=(const ModInt &p) {
		if((x += mod - p.x) >= mod) x -= mod;
		return *this;
	}

	ModInt &operator*=(const ModInt &p) {
		x = (int)(1LL * x * p.x % mod);
		return *this;
	}

	ModInt &operator/=(const ModInt &p) {
		*this *= p.inverse();
		return *this;
	}

	ModInt operator-() const { return ModInt(-x); }

	ModInt operator+(const ModInt &p) const { return ModInt(*this) += p; }

	ModInt operator-(const ModInt &p) const { return ModInt(*this) -= p; }

	ModInt operator*(const ModInt &p) const { return ModInt(*this) *= p; }

	ModInt operator/(const ModInt &p) const { return ModInt(*this) /= p; }

	bool operator==(const ModInt &p) const { return x == p.x; }

	bool operator!=(const ModInt &p) const { return x != p.x; }

	ModInt inverse() const {
		int a = x, b = mod, u = 1, v = 0, t;
		while(b > 0) {
			t = a / b;
			swap(a -= t * b, b);
			swap(u -= t * v, v);
		}
		return ModInt(u);
	}

	ModInt pow(int64_t n) const {
		ModInt ret(1), mul(x);
		while(n > 0) {
			if(n & 1) ret *= mul;
			mul *= mul;
			n >>= 1;
		}
		return ret;
	}

	friend ostream &operator<<(ostream &os, const ModInt &p) {
		return os << p.x;
	}

	friend istream &operator>>(istream &is, ModInt &a) {
		int64_t t;
		is >> t;
		a = ModInt<mod>(t);
		return (is);
	}

	static int get_mod() { return mod; }
};

using mint = ModInt<mod>;

struct UnionFind{
	vl p;
	vl rank;
	vl cnt;

	UnionFind(ll n){
		rank.resize(n,0);
		p.resize(n,0);
		cnt.resize(n,0);
		rep(i,n){
			p[i] = i;
			rank[i] = 0;
			cnt[i] = 1;
		}
	}

	ll find(ll x){
		if(x != p[x]) p[x] = find(p[x]);
		return p[x];
	}

	bool same(ll x, ll y){
		return find(x) == find(y);
	}

	void unite(ll x, ll y){
		x = find(x);
		y = find(y);
		if(x == y) return;
		if(rank[x] > rank[y]){
			p[y] = x;
			cnt[x] += cnt[y];
		}else{
			p[x] = y;
			cnt[y] += cnt[x];
			if(rank[x] == rank[y]) rank[y]++;
		}
	}

	ll size(ll x){
		return cnt[find(x)];
	}
};

int main(){
	int n,m; cin >> n >> m;
	string s; cin >> s;
	vector<char> c(m);
	vl u(m);
	rep(i,m) cin >> c[i] >> u[i];
	vl rev(200);
	rep(i,m) rev[c[i]] = i;
	mint ans = 0;
	rep(two,1<<m){ //2桁になるものはどれか
		vector<int> a;
		for(auto x : s){
			if(isdigit(x)) a.push_back(x-'0');
			else if(two>>rev[x] & 1){
				a.push_back(rev[x]+m+10);
				a.push_back(rev[x]+10);
			}else{
				a.push_back(rev[x]+10);
			}
		}
		int N = a.size();
		UnionFind uf(m*2+10);
		rep(i,N/2){
			uf.unite(a[i],a[N-i-1]);
		}
		vector<vector<int>> gr(m*2+10);
		rep(i,m*2+10) gr[uf.find(i)].push_back(i);

		rep(bit,1<<m){ //10の位がギリギリのもの
			bool f = true;
			rep(i,m){
				if((two>>i & 1) && u[i] < 10) f = false;
				if((two>>i & 1 ^ 1) && (bit>>i & 1)) f = false;
			}
			if(!f) continue;
			mint res = 1;
			rep(i,m*2+10){
				if(uf.find(i) != i) continue;
				if(i>=m+10 && (two>>(i-m-10)&1^1)) continue;
				int L = 0, R = 9;
				for(auto x : gr[i]){
					if(x < 10){
						chmax(L,x); chmin(R,x);
					}else if(x < m+10){
						int id = x-10;
						if(bit>>id & 1) chmin(R,u[id]%10);
						else if(u[id] < 10) chmin(R,u[id]);
					}else{
						int id = x-m-10;
						if(bit>>id & 1) chmax(L,u[id]/10), chmin(R,u[id]/10);
						else chmax(L,1), chmin(R,u[id]/10-1);
					}
				}
				res *= max(0,R-L+1);
			}
			ans += res;
		}
	}
	cout << ans << endl;
}
accuracy: 1
time_ms: 20
memory_kb: 3464
final_rank: 5

code_snippet: #pragma GCC ta g("avx2")
#pragma GCC optimize("Ofast")
#pragma GCC optimize("unroll-loops")
#include <bits/stdc++.h>
using namespace std;
#define DEBUG
#ifdef DEBUG
template <class T, class U>
ostream &operator<<(ostream &os, const pair<T, U> &p) {
    os << '(' << p.first << ',' << p.second << ')';
    return os;
}
template <class T> ostream &operator<<(ostream &os, const vector<T> &v) {
    os << '{';
    for(int i = 0; i < (int)v.size(); i++) {
        if(i) { os << ','; }
        os << v[i];
    }
    os << '}';
    return os;
}
void debugg() { cerr << endl; }
template <class T, class... Args>
void debugg(const T &x, const Args &... args) {
    cerr << " " << x;
    debugg(args...);
}
#define debug(...)                                                             \
    cerr << __LINE__ << " [" << #__VA_ARGS__ << "]: ", debugg(__VA_ARGS__)
#define dump(x) cerr << __LINE__ << " " << #x << " = " << (x) << endl
#else
#define debug(...) (void(0))
#define dump(x) (void(0))
#endif
using namespace std;
typedef long long ll;
typedef vector<ll> vl;
typedef vector<vl> vvl;
typedef vector<char> vc;
typedef vector<string> vs;
typedef vector<bool> vb;
typedef vector<double> vd;
typedef pair<ll,ll> P;
typedef pair<int,int> pii;
typedef vector<P> vpl;
typedef tuple<ll,ll,ll> tapu;
#define rep(i,n) for(int i=0; i<(n); i++)
#define REP(i,a,b) for(int i=(a); i<(b); i++)
#define all(x) (x).begin(), (x).end()
#define rall(x) (x).rbegin(), (x).rend()
template<typename T1,typename T2> inline bool chmin(T1 &a,T2 b){
	if(a>b){
		a = b; return true;
	}
	else return false;
}
template<typename T1,typename T2> inline bool chmax(T1 &a,T2 b){
	if(a<b){
		a = b; return true;
	}
	else return false;
}
template<typename T> inline void print(T &a){
    int sz = a.size();
    for(auto itr = a.begin(); itr != a.end(); itr++){
		cout << *itr;
        sz--;
        if(sz) cout << " ";
	}
    cout << "\n";
}
template<typename T1,typename T2> inline void print2(T1 a, T2 b){
	cout << a << " " << b << "\n";
}
template<typename T1,typename T2,typename T3> inline void print3(T1 a, T2 b, T3 c){
	cout << a << " " << b << " " << c << "\n";
}
void mark() {cout << "#" << "\n";}
ll pcount(ll x) {return __builtin_popcountll(x);}
//#include <atcoder/maxflow>
//using namespace atcoder;
const int inf = (1<<30)-1;
const ll linf = 1LL<<61;
const int MAX = 510000;
int dy[8] = {0,1,0,-1,1,-1,-1,1};
int dx[8] = {-1,0,1,0,1,-1,1,-1};
const double pi = acos(-1);
const double eps = 1e-9;
const int mod = 1e9 + 7;
//const int mod = 998244353;

template <int mod> struct ModInt {
	int x;

	ModInt() : x(0) {}

	ModInt(int64_t y) : x(y >= 0 ? y % mod : (mod - (-y) % mod) % mod) {}

	ModInt &operator+=(const ModInt &p) {
		if((x += p.x) >= mod) x -= mod;
		return *this;
	}

	ModInt &operator-=(const ModInt &p) {
		if((x += mod - p.x) >= mod) x -= mod;
		return *this;
	}

	ModInt &operator*=(const ModInt &p) {
		x = (int)(1LL * x * p.x % mod);
		return *this;
	}

	ModInt &operator/=(const ModInt &p) {
		*this *= p.inverse();
		return *this;
	}

	ModInt operator-() const { return ModInt(-x); }

	ModInt operator+(const ModInt &p) const { return ModInt(*this) += p; }

	ModInt operator-(const ModInt &p) const { return ModInt(*this) -= p; }

	ModInt operator*(const ModInt &p) const { return ModInt(*this) *= p; }

	ModInt operator/(const ModInt &p) const { return ModInt(*this) /= p; }

	bool operator==(const ModInt &p) const { return x == p.x; }

	bool operator!=(const ModInt &p) const { return x != p.x; }

	ModInt inverse() const {
		int a = x, b = mod, u = 1, v = 0, t;
		while(b > 0) {
			t = a / b;
			swap(a -= t * b, b);
			swap(u -= t * v, v);
		}
		return ModInt(u);
	}

	ModInt pow(int64_t n) const {
		ModInt ret(1), mul(x);
		while(n > 0) {
			if(n & 1) ret *= mul;
			mul *= mul;
			n >>= 1;
		}
		return ret;
	}

	friend ostream &operator<<(ostream &os, const ModInt &p) {
		return os << p.x;
	}

	friend istream &operator>>(istream &is, ModInt &a) {
		int64_t t;
		is >> t;
		a = ModInt<mod>(t);
		return (is);
	}

	static int get_mod() { return mod; }
};

using mint = ModInt<mod>;

struct UnionFind{
	vl p;
	vl rank;
	vl cnt;

	UnionFind(ll n){
		rank.resize(n,0);
		p.resize(n,0);
		cnt.resize(n,0);
		rep(i,n){
			p[i] = i;
			rank[i] = 0;
			cnt[i] = 1;
		}
	}

	ll find(ll x){
		if(x != p[x]) p[x] = find(p[x]);
		return p[x];
	}

	bool same(ll x, ll y){
		return find(x) == find(y);
	}

	void unite(ll x, ll y){
		x = find(x);
		y = find(y);
		if(x == y) return;
		if(rank[x] > rank[y]){
			p[y] = x;
			cnt[x] += cnt[y];
		}else{
			p[x] = y;
			cnt[y] += cnt[x];
			if(rank[x] == rank[y]) rank[y]++;
		}
	}

	ll size(ll x){
		return cnt[find(x)];
	}
};

int main(){
	int n,m; cin >> n >> m;
	string s; cin >> s;
	vector<char> c(m);
	vl u(m);
	rep(i,m) cin >> c[i] >> u[i];
	vl rev(200);
	rep(i,m) rev[c[i]] = i;
	mint ans = 0;
	rep(two,1<<m){ //2桁になるものはどれか
		vector<int> a;
		for(auto x : s){
			if(isdigit(x)) a.push_back(x-'0');
			else if(two>>rev[x] & 1){
				a.push_back(rev[x]+m+10);
				a.push_back(rev[x]+10);
			}else{
				a.push_back(rev[x]+10);
			}
		}
		int N = a.size();
		UnionFind uf(m*2+10);
		rep(i,N/2){
			uf.unite(a[i],a[N-i-1]);
		}
		vector<vector<int>> gr(m*2+10);
		rep(i,m*2+10) gr[uf.find(i)].push_back(i);

		rep(bit,1<<m){ //10の位がギリギリのもの
			bool f = true;
			rep(i,m){
				if((two>>i & 1) && u[i] < 10) f = false;
				if((two>>i & 1 ^ 1) && (bit>>i & 1)) f = false;
			}
			if(!f) continue;
			mint res = 1;
			rep(i,m*2+10){
				if(uf.find(i) != i) continue;
				if(i>=m+10 && (bit>>(i-m-10)&1^1)) continue;
				int L = 0, R = 9;
				for(auto x : gr[i]){
					if(x < 10){
						chmax(L,x); chmin(R,x);
					}else if(x < m+10){
						int id = x-10;
						if(bit>>id & 1) chmin(R,u[id]%10);
						else if(u[id] < 10) chmin(R,u[id]);
					}else{
						int id = x-m-10;
						if(bit>>id & 1) chmax(L,u[id]/10), chmin(R,u[id]/10);
						else chmax(L,1), chmin(R,u[id]/10-1);
					}
				}
				res *= max(0,R-L+1);
			}
			ans += res;
		}
	}
	cout << ans << endl;
}
accuracy: 0.06976744186046512
time_ms: 10
memory_kb: 3456
final_rank: 20

code_snippet: #include <bits/stdc++.h>
using namespace std;
typedef long long ll;
template<class T>bool chmax(T &a, const T &b) { if (a<b) { a=b; return true; } return false; }
template<class T>bool chmin(T &a, const T &b) { if (b<a) { a=b; return true; } return false; }
#define all(x) (x).begin(),(x).end()
#define fi first
#define se second
#define mp make_pair
#define si(x) int(x.size())
const int mod=1000000007,MAX=1<<20,INF=1<<29;

//modintのみ

// from: https://gist.github.com/yosupo06/ddd51afb727600fd95d9d8ad6c3c80c9
// (based on AtCoder STL)

#include <cassert>
#include <numeric>
#include <type_traits>

namespace atcoder {
    
    namespace internal {
        
#ifndef _MSC_VER
        template <class T>
        using is_signed_int128 =
        typename std::conditional<std::is_same<T, __int128_t>::value ||
        std::is_same<T, __int128>::value,
        std::true_type,
        std::false_type>::type;
        
        template <class T>
        using is_unsigned_int128 =
        typename std::conditional<std::is_same<T, __uint128_t>::value ||
        std::is_same<T, unsigned __int128>::value,
        std::true_type,
        std::false_type>::type;
        
        template <class T>
        using make_unsigned_int128 =
        typename std::conditional<std::is_same<T, __int128_t>::value,
        __uint128_t,
        unsigned __int128>;
        
        template <class T>
        using is_integral = typename std::conditional<std::is_integral<T>::value ||
        is_signed_int128<T>::value ||
        is_unsigned_int128<T>::value,
        std::true_type,
        std::false_type>::type;
        
        template <class T>
        using is_signed_int = typename std::conditional<(is_integral<T>::value &&
                                                         std::is_signed<T>::value) ||
        is_signed_int128<T>::value,
        std::true_type,
        std::false_type>::type;
        
        template <class T>
        using is_unsigned_int =
        typename std::conditional<(is_integral<T>::value &&
                                   std::is_unsigned<T>::value) ||
        is_unsigned_int128<T>::value,
        std::true_type,
        std::false_type>::type;
        
        template <class T>
        using to_unsigned = typename std::conditional<
        is_signed_int128<T>::value,
        make_unsigned_int128<T>,
        typename std::conditional<std::is_signed<T>::value,
        std::make_unsigned<T>,
        std::common_type<T>>::type>::type;
        
#else
        
        template <class T> using is_integral = typename std::is_integral<T>;
        
        template <class T>
        using is_signed_int =
        typename std::conditional<is_integral<T>::value && std::is_signed<T>::value,
        std::true_type,
        std::false_type>::type;
        
        template <class T>
        using is_unsigned_int =
        typename std::conditional<is_integral<T>::value &&
        std::is_unsigned<T>::value,
        std::true_type,
        std::false_type>::type;
        
        template <class T>
        using to_unsigned = typename std::conditional<is_signed_int<T>::value,
        std::make_unsigned<T>,
        std::common_type<T>>::type;
        
#endif
        
        template <class T>
        using is_signed_int_t = std::enable_if_t<is_signed_int<T>::value>;
        
        template <class T>
        using is_unsigned_int_t = std::enable_if_t<is_unsigned_int<T>::value>;
        
        template <class T> using to_unsigned_t = typename to_unsigned<T>::type;
        
    }  // namespace internal
    
}  // namespace atcoder

#include <utility>

namespace atcoder {
    
    namespace internal {
        
        constexpr long long safe_mod(long long x, long long m) {
            x %= m;
            if (x < 0) x += m;
            return x;
        }
        
        struct barrett {
            unsigned int _m;
            unsigned long long im;
            
            barrett(unsigned int m) : _m(m), im((unsigned long long)(-1) / m + 1) {}
            
            unsigned int umod() const { return _m; }
            
            unsigned int mul(unsigned int a, unsigned int b) const {
                
                unsigned long long z = a;
                z *= b;
#ifdef _MSC_VER
                unsigned long long x;
                _umul128(z, im, &x);
#else
                unsigned long long x =
                (unsigned long long)(((unsigned __int128)(z)*im) >> 64);
#endif
                unsigned int v = (unsigned int)(z - x * _m);
                if (_m <= v) v += _m;
                return v;
            }
        };
        
        constexpr long long pow_mod_constexpr(long long x, long long n, int m) {
            if (m == 1) return 0;
            unsigned int _m = (unsigned int)(m);
            unsigned long long r = 1;
            unsigned long long y = safe_mod(x, m);
            while (n) {
                if (n & 1) r = (r * y) % _m;
                y = (y * y) % _m;
                n >>= 1;
            }
            return r;
        }
        
        constexpr bool is_prime_constexpr(int n) {
            if (n <= 1) return false;
            if (n == 2 || n == 7 || n == 61) return true;
            if (n % 2 == 0) return false;
            long long d = n - 1;
            while (d % 2 == 0) d /= 2;
            for (long long a : {2, 7, 61}) {
                long long t = d;
                long long y = pow_mod_constexpr(a, t, n);
                while (t != n - 1 && y != 1 && y != n - 1) {
                    y = y * y % n;
                    t <<= 1;
                }
                if (y != n - 1 && t % 2 == 0) {
                    return false;
                }
            }
            return true;
        }
        template <int n> constexpr bool is_prime = is_prime_constexpr(n);
        
        constexpr std::pair<long long, long long> inv_gcd(long long a, long long b) {
            a = safe_mod(a, b);
            if (a == 0) return {b, 0};
            
            long long s = b, t = a;
            long long m0 = 0, m1 = 1;
            
            while (t) {
                long long u = s / t;
                s -= t * u;
                m0 -= m1 * u;  // |m1 * u| <= |m1| * s <= b
                
                
                auto tmp = s;
                s = t;
                t = tmp;
                tmp = m0;
                m0 = m1;
                m1 = tmp;
            }
            if (m0 < 0) m0 += b / s;
            return {s, m0};
        }
        
        constexpr int primitive_root_constexpr(int m) {
            if (m == 2) return 1;
            if (m == 167772161) return 3;
            if (m == 469762049) return 3;
            if (m == 754974721) return 11;
            if (m == 998244353) return 3;
            int divs[20] = {};
            divs[0] = 2;
            int cnt = 1;
            int x = (m - 1) / 2;
            while (x % 2 == 0) x /= 2;
            for (int i = 3; (long long)(i)*i <= x; i += 2) {
                if (x % i == 0) {
                    divs[cnt++] = i;
                    while (x % i == 0) {
                        x /= i;
                    }
                }
            }
            if (x > 1) {
                divs[cnt++] = x;
            }
            for (int g = 2;; g++) {
                bool ok = true;
                for (int i = 0; i < cnt; i++) {
                    if (pow_mod_constexpr(g, (m - 1) / divs[i], m) == 1) {
                        ok = false;
                        break;
                    }
                }
                if (ok) return g;
            }
        }
        template <int m> constexpr int primitive_root = primitive_root_constexpr(m);
        
    }  // namespace internal
    
}  // namespace atcoder

#include <cassert>
#include <numeric>
#include <type_traits>

#ifdef _MSC_VER
#include <intrin.h>
#endif

namespace atcoder {
    
    namespace internal {
        
        struct modint_base {};
        struct static_modint_base : modint_base {};
        
        template <class T> using is_modint = std::is_base_of<modint_base, T>;
        template <class T> using is_modint_t = std::enable_if_t<is_modint<T>::value>;
        
    }  // namespace internal
    
    template <int m, std::enable_if_t<(1 <= m)>* = nullptr>
    struct static_modint : internal::static_modint_base {
        using mint = static_modint;
        
    public:
        static constexpr int mod() { return m; }
        static mint raw(int v) {
            mint x;
            x._v = v;
            return x;
        }
        
        static_modint() : _v(0) {}
        template <class T, internal::is_signed_int_t<T>* = nullptr>
        static_modint(T v) {
            long long x = (long long)(v % (long long)(umod()));
            if (x < 0) x += umod();
            _v = (unsigned int)(x);
        }
        template <class T, internal::is_unsigned_int_t<T>* = nullptr>
        static_modint(T v) {
            _v = (unsigned int)(v % umod());
        }
        static_modint(bool v) { _v = ((unsigned int)(v) % umod()); }
        
        unsigned int val() const { return _v; }
        
        mint& operator++() {
            _v++;
            if (_v == umod()) _v = 0;
            return *this;
        }
        mint& operator--() {
            if (_v == 0) _v = umod();
            _v--;
            return *this;
        }
        mint operator++(int) {
            mint result = *this;
            ++*this;
            return result;
        }
        mint operator--(int) {
            mint result = *this;
            --*this;
            return result;
        }
        
        mint& operator+=(const mint& rhs) {
            _v += rhs._v;
            if (_v >= umod()) _v -= umod();
            return *this;
        }
        mint& operator-=(const mint& rhs) {
            _v -= rhs._v;
            if (_v >= umod()) _v += umod();
            return *this;
        }
        mint& operator*=(const mint& rhs) {
            unsigned long long z = _v;
            z *= rhs._v;
            _v = (unsigned int)(z % umod());
            return *this;
        }
        mint& operator/=(const mint& rhs) { return *this = *this * rhs.inv(); }
        
        mint operator+() const { return *this; }
        mint operator-() const { return mint() - *this; }
        
        mint pow(long long n) const {
            assert(0 <= n);
            mint x = *this, r = 1;
            while (n) {
                if (n & 1) r *= x;
                x *= x;
                n >>= 1;
            }
            return r;
        }
        mint inv() const {
            if (prime) {
                assert(_v);
                return pow(umod() - 2);
            } else {
                auto eg = internal::inv_gcd(_v, m);
                assert(eg.first == 1);
                return eg.second;
            }
        }
        
        friend mint operator+(const mint& lhs, const mint& rhs) {
            return mint(lhs) += rhs;
        }
        friend mint operator-(const mint& lhs, const mint& rhs) {
            return mint(lhs) -= rhs;
        }
        friend mint operator*(const mint& lhs, const mint& rhs) {
            return mint(lhs) *= rhs;
        }
        friend mint operator/(const mint& lhs, const mint& rhs) {
            return mint(lhs) /= rhs;
        }
        friend bool operator==(const mint& lhs, const mint& rhs) {
            return lhs._v == rhs._v;
        }
        friend bool operator!=(const mint& lhs, const mint& rhs) {
            return lhs._v != rhs._v;
        }
        
    private:
        unsigned int _v;
        static constexpr unsigned int umod() { return m; }
        static constexpr bool prime = internal::is_prime<m>;
    };
    
    template <int id> struct dynamic_modint : internal::modint_base {
        using mint = dynamic_modint;
        
    public:
        static int mod() { return (int)(bt.umod()); }
        static void set_mod(int m) {
            assert(1 <= m);
            bt = internal::barrett(m);
        }
        static mint raw(int v) {
            mint x;
            x._v = v;
            return x;
        }
        
        dynamic_modint() : _v(0) {}
        template <class T, internal::is_signed_int_t<T>* = nullptr>
        dynamic_modint(T v) {
            long long x = (long long)(v % (long long)(mod()));
            if (x < 0) x += mod();
            _v = (unsigned int)(x);
        }
        template <class T, internal::is_unsigned_int_t<T>* = nullptr>
        dynamic_modint(T v) {
            _v = (unsigned int)(v % mod());
        }
        dynamic_modint(bool v) { _v = ((unsigned int)(v) % mod()); }
        
        unsigned int val() const { return _v; }
        
        mint& operator++() {
            _v++;
            if (_v == umod()) _v = 0;
            return *this;
        }
        mint& operator--() {
            if (_v == 0) _v = umod();
            _v--;
            return *this;
        }
        mint operator++(int) {
            mint result = *this;
            ++*this;
            return result;
        }
        mint operator--(int) {
            mint result = *this;
            --*this;
            return result;
        }
        
        mint& operator+=(const mint& rhs) {
            _v += rhs._v;
            if (_v >= umod()) _v -= umod();
            return *this;
        }
        mint& operator-=(const mint& rhs) {
            _v += mod() - rhs._v;
            if (_v >= umod()) _v -= umod();
            return *this;
        }
        mint& operator*=(const mint& rhs) {
            _v = bt.mul(_v, rhs._v);
            return *this;
        }
        mint& operator/=(const mint& rhs) { return *this = *this * rhs.inv(); }
        
        mint operator+() const { return *this; }
        mint operator-() const { return mint() - *this; }
        
        mint pow(long long n) const {
            assert(0 <= n);
            mint x = *this, r = 1;
            while (n) {
                if (n & 1) r *= x;
                x *= x;
                n >>= 1;
            }
            return r;
        }
        mint inv() const {
            auto eg = internal::inv_gcd(_v, mod());
            assert(eg.first == 1);
            return eg.second;
        }
        
        friend mint operator+(const mint& lhs, const mint& rhs) {
            return mint(lhs) += rhs;
        }
        friend mint operator-(const mint& lhs, const mint& rhs) {
            return mint(lhs) -= rhs;
        }
        friend mint operator*(const mint& lhs, const mint& rhs) {
            return mint(lhs) *= rhs;
        }
        friend mint operator/(const mint& lhs, const mint& rhs) {
            return mint(lhs) /= rhs;
        }
        friend bool operator==(const mint& lhs, const mint& rhs) {
            return lhs._v == rhs._v;
        }
        friend bool operator!=(const mint& lhs, const mint& rhs) {
            return lhs._v != rhs._v;
        }
        
    private:
        unsigned int _v;
        static internal::barrett bt;
        static unsigned int umod() { return bt.umod(); }
    };
    template <int id> internal::barrett dynamic_modint<id>::bt = 998244353;
    
    using modint998244353 = static_modint<998244353>;
    using modint1000000007 = static_modint<1000000007>;
    using modint = dynamic_modint<-1>;
    
    namespace internal {
        
        template <class T>
        using is_static_modint = std::is_base_of<internal::static_modint_base, T>;
        
        template <class T>
        using is_static_modint_t = std::enable_if_t<is_static_modint<T>::value>;
        
        template <class> struct is_dynamic_modint : public std::false_type {};
        template <int id>
        struct is_dynamic_modint<dynamic_modint<id>> : public std::true_type {};
        
        template <class T>
        using is_dynamic_modint_t = std::enable_if_t<is_dynamic_modint<T>::value>;
        
    }  // namespace internal
    
}  // namespace atcoder

using mint=atcoder::modint1000000007;

//Union Find

struct UF{
    int n;
    vector<int> par,size;
    
    void init(int n_){
        n=n_;
        par.assign(n,-1);
        size.assign(n,1);
        
        for(int i=0;i<n;i++){
            par[i]=i;
        }
    }
    
    int root(int a){
        if(par[a]==a) return a;
        else return par[a]=root(par[a]);
    }
    
    void unite(int a,int b){
        if(root(a)!=root(b)){
            if(root(a)>root(b)) swap(a,b);
            size[root(a)]+=size[root(b)];
            par[root(b)]=root(a);
        }
    }
    
    bool check(int a,int b){
        return root(a)==root(b);
    }
};

int main(){
    
    std::ifstream in("text.txt");
    std::cin.rdbuf(in.rdbuf());
    cin.tie(0);
    ios::sync_with_stdio(false);
    
    int N,M;cin>>N>>M;
    string S;cin>>S;
    vector<char> A(M);
    vector<int> B(M);
    map<char,int> MA;
    for(int i=0;i<M;i++){
        cin>>A[i]>>B[i];
        MA[A[i]]=i;
    }
    for(int i=0;i<M;i++) A[i]=char('a'+i);
    for(int i=0;i<N;i++){
        if(!('0'<=S[i]&&S[i]<='9')) S[i]=char('a'+MA[S[i]]);
    }
    
    mint ans=0;
    
    for(int bit=0;bit<(1<<M);bit++){
        bool ok=true;
        for(int i=0;i<M;i++){
            if(bit&(1<<i)){
                if(B[i]<=9){
                    ok=false;
                }
            }
        }
        if(!ok) continue;
        vector<int> T,use(20);
        for(char c:S){
            if('0'<=c&&c<='9') T.push_back(20+int(c-'0'));
            else{
                int i=int(c-'a');
                if(bit&(1<<i)){
                    T.push_back(10+i);
                    T.push_back(i);
                    use[i]=true;
                    use[10+i]=true;
                }else{
                    T.push_back(i);
                    use[i]=true;
                }
            }
        }
        
        UF uf;uf.init(30);
        vector<int> fix(20,-1);
        for(int i=0;i<si(T);i++){
            uf.unite(T[i],T[si(T)-1-i]);
        }
        
        for(int i=0;i<20;i++){
            for(int j=0;j<10;j++){
                if(uf.check(i,20+j)){
                    if(fix[i]!=-1) ok=false;
                    fix[i]=j;
                }
            }
            if(i>=10&&use[i]&&fix[i]==0) ok=false;
            if(i>=10&&use[i]&&fix[i]>B[i-10]/10) ok=false;
        }
        
        for(int i=0;i<10;i++) for(int j=i+1;j<10;j++) if(uf.check(20+i,20+j)) ok=false;
        
        if(!ok) continue;
        
        set<int> ro;
        for(int i=0;i<20;i++) if(use[i]) ro.insert(uf.root(i));
        
        for(int Y=bit;;Y=(Y-1)&bit){
            int X=bit-Y;
            vector<int> l(20,-1),r(20,10);
            for(int i=0;i<M;i++){
                if(!use[10+i]) continue;
                int a=uf.root(10+i);
                if(X&(1<<i)){
                    chmax(l[a],1);
                    chmin(r[a],B[i]/10-1);
                }else{
                    chmax(l[a],B[i]/10);
                    chmin(r[a],B[i]/10);
                }
                if(fix[10+i]!=-1){
                    chmax(l[a],fix[10+i]);
                    chmin(r[a],fix[10+i]);
                }
            }
            for(int i=0;i<M;i++){
                int a=uf.root(i);
                if(!(bit&(1<<i))){
                    chmax(l[a],0);
                    chmin(r[a],min(9,B[i]));
                }else if(X&(1<<i)){
                    chmax(l[a],0);
                    chmin(r[a],9);
                }else{
                    chmax(l[a],0);
                    chmin(r[a],B[i]%10);
                }
                if(fix[i]!=-1){
                    chmax(l[a],fix[i]);
                    chmin(r[a],fix[i]);
                }
            }
            
            mint tasu=1;
            
            for(int a:ro){
                if(l[a]>r[a]) tasu=0;
                tasu*=(r[a]-l[a]+1);
            }
            
            ans+=tasu;
            
            if(Y==0) break;
            // Yがギリギリ Xが余裕あり
        }
    }
    
    cout<<ans.val()<<endl;
}
accuracy: 1
time_ms: 10
memory_kb: 3464
final_rank: 1

code_snippet: #include <bits/stdc++.h>
using namespace std;
typedef long long ll;
template<class T>bool chmax(T &a, const T &b) { if (a<b) { a=b; return true; } return false; }
template<class T>bool chmin(T &a, const T &b) { if (b<a) { a=b; return true; } return false; }
#define all(x) (x).begin(),(x).end()
#define fi first
#define se second
#define mp make_pair
#define si(x) int(x.size())
const int mod=1000000007,MAX=1<<20,INF=1<<29;

//modintのみ

// from: https://gist.github.com/yosupo06/ddd51afb727600fd95d9d8ad6c3c80c9
// (based on AtCoder STL)

#include <cassert>
#include <numeric>
#include <type_traits>

namespace atcoder {
    
    namespace internal {
        
#ifndef _MSC_VER
        template <class T>
        using is_signed_int128 =
        typename std::conditional<std::is_same<T, __int128_t>::value ||
        std::is_same<T, __int128>::value,
        std::true_type,
        std::false_type>::type;
        
        template <class T>
        using is_unsigned_int128 =
        typename std::conditional<std::is_same<T, __uint128_t>::value ||
        std::is_same<T, unsigned __int128>::value,
        std::true_type,
        std::false_type>::type;
        
        template <class T>
        using make_unsigned_int128 =
        typename std::conditional<std::is_same<T, __int128_t>::value,
        __uint128_t,
        unsigned __int128>;
        
        template <class T>
        using is_integral = typename std::conditional<std::is_integral<T>::value ||
        is_signed_int128<T>::value ||
        is_unsigned_int128<T>::value,
        std::true_type,
        std::false_type>::type;
        
        template <class T>
        using is_signed_int = typename std::conditional<(is_integral<T>::value &&
                                                         std::is_signed<T>::value) ||
        is_signed_int128<T>::value,
        std::true_type,
        std::false_type>::type;
        
        template <class T>
        using is_unsigned_int =
        typename std::conditional<(is_integral<T>::value &&
                                   std::is_unsigned<T>::value) ||
        is_unsigned_int128<T>::value,
        std::true_type,
        std::false_type>::type;
        
        template <class T>
        using to_unsigned = typename std::conditional<
        is_signed_int128<T>::value,
        make_unsigned_int128<T>,
        typename std::conditional<std::is_signed<T>::value,
        std::make_unsigned<T>,
        std::common_type<T>>::type>::type;
        
#else
        
        template <class T> using is_integral = typename std::is_integral<T>;
        
        template <class T>
        using is_signed_int =
        typename std::conditional<is_integral<T>::value && std::is_signed<T>::value,
        std::true_type,
        std::false_type>::type;
        
        template <class T>
        using is_unsigned_int =
        typename std::conditional<is_integral<T>::value &&
        std::is_unsigned<T>::value,
        std::true_type,
        std::false_type>::type;
        
        template <class T>
        using to_unsigned = typename std::conditional<is_signed_int<T>::value,
        std::make_unsigned<T>,
        std::common_type<T>>::type;
        
#endif
        
        template <class T>
        using is_signed_int_t = std::enable_if_t<is_signed_int<T>::value>;
        
        template <class T>
        using is_unsigned_int_t = std::enable_if_t<is_unsigned_int<T>::value>;
        
        template <class T> using to_unsigned_t = typename to_unsigned<T>::type;
        
    }  // namespace internal
    
}  // namespace atcoder

#include <utility>

namespace atcoder {
    
    namespace internal {
        
        constexpr long long safe_mod(long long x, long long m) {
            x %= m;
            if (x < 0) x += m;
            return x;
        }
        
        struct barrett {
            unsigned int _m;
            unsigned long long im;
            
            barrett(unsigned int m) : _m(m), im((unsigned long long)(-1) / m + 1) {}
            
            unsigned int umod() const { return _m; }
            
            unsigned int mul(unsigned int a, unsigned int b) const {
                
                unsigned long long z = a;
                z *= b;
#ifdef _MSC_VER
                unsigned long long x;
                _umul128(z, im, &x);
#else
                unsigned long long x =
                (unsigned long long)(((unsigned __int128)(z)*im) >> 64);
#endif
                unsigned int v = (unsigned int)(z - x * _m);
                if (_m <= v) v += _m;
                return v;
            }
        };
        
        constexpr long long pow_mod_constexpr(long long x, long long n, int m) {
            if (m == 1) return 0;
            unsigned int _m = (unsigned int)(m);
            unsigned long long r = 1;
            unsigned long long y = safe_mod(x, m);
            while (n) {
                if (n & 1) r = (r * y) % _m;
                y = (y * y) % _m;
                n >>= 1;
            }
            return r;
        }
        
        constexpr bool is_prime_constexpr(int n) {
            if (n <= 1) return false;
            if (n == 2 || n == 7 || n == 61) return true;
            if (n % 2 == 0) return false;
            long long d = n - 1;
            while (d % 2 == 0) d /= 2;
            for (long long a : {2, 7, 61}) {
                long long t = d;
                long long y = pow_mod_constexpr(a, t, n);
                while (t != n - 1 && y != 1 && y != n - 1) {
                    y = y * y % n;
                    t <<= 1;
                }
                if (y != n - 1 && t % 2 == 0) {
                    return false;
                }
            }
            return true;
        }
        template <int n> constexpr bool is_prime = is_prime_constexpr(n);
        
        constexpr std::pair<long long, long long> inv_gcd(long long a, long long b) {
            a = safe_mod(a, b);
            if (a == 0) return {b, 0};
            
            long long s = b, t = a;
            long long m0 = 0, m1 = 1;
            
            while (t) {
                long long u = s / t;
                s -= t * u;
                m0 -= m1 * u;  // |m1 * u| <= |m1| * s <= b
                
                
                auto tmp = s;
                s = t;
                t = tmp;
                tmp = m0;
                m0 = m1;
                m1 = tmp;
            }
            if (m0 < 0) m0 += b / s;
            return {s, m0};
        }
        
        constexpr int primitive_root_constexpr(int m) {
            if (m == 2) return 1;
            if (m == 167772161) return 3;
            if (m == 469762049) return 3;
            if (m == 754974721) return 11;
            if (m == 998244353) return 3;
            int divs[20] = {};
            divs[0] = 2;
            int cnt = 1;
            int x = (m - 1) / 2;
            while (x % 2 == 0) x /= 2;
            for (int i = 3; (long long)(i)*i <= x; i += 2) {
                if (x % i == 0) {
                    divs[cnt++] = i;
                    while (x % i == 0) {
                        x /= i;
                    }
                }
            }
            if (x > 1) {
                divs[cnt++] = x;
            }
            for (int g = 2;; g++) {
                bool ok = true;
                for (int i = 0; i < cnt; i++) {
                    if (pow_mod_constexpr(g, (m - 1) / divs[i], m) == 1) {
                        ok = false;
                        break;
                    }
                }
                if (ok) return g;
            }
        }
        template <int m> constexpr int primitive_root = primitive_root_constexpr(m);
        
    }  // namespace internal
    
}  // namespace atcoder

#include <cassert>
#include <numeric>
#include <type_traits>

#ifdef _MSC_VER
#include <intrin.h>
#endif

namespace atcoder {
    
    namespace internal {
        
        struct modint_base {};
        struct static_modint_base : modint_base {};
        
        template <class T> using is_modint = std::is_base_of<modint_base, T>;
        template <class T> using is_modint_t = std::enable_if_t<is_modint<T>::value>;
        
    }  // namespace internal
    
    template <int m, std::enable_if_t<(1 <= m)>* = nullptr>
    struct static_modint : internal::static_modint_base {
        using mint = static_modint;
        
    public:
        static constexpr int mod() { return m; }
        static mint raw(int v) {
            mint x;
            x._v = v;
            return x;
        }
        
        static_modint() : _v(0) {}
        template <class T, internal::is_signed_int_t<T>* = nullptr>
        static_modint(T v) {
            long long x = (long long)(v % (long long)(umod()));
            if (x < 0) x += umod();
            _v = (unsigned int)(x);
        }
        template <class T, internal::is_unsigned_int_t<T>* = nullptr>
        static_modint(T v) {
            _v = (unsigned int)(v % umod());
        }
        static_modint(bool v) { _v = ((unsigned int)(v) % umod()); }
        
        unsigned int val() const { return _v; }
        
        mint& operator++() {
            _v++;
            if (_v == umod()) _v = 0;
            return *this;
        }
        mint& operator--() {
            if (_v == 0) _v = umod();
            _v--;
            return *this;
        }
        mint operator++(int) {
            mint result = *this;
            ++*this;
            return result;
        }
        mint operator--(int) {
            mint result = *this;
            --*this;
            return result;
        }
        
        mint& operator+=(const mint& rhs) {
            _v += rhs._v;
            if (_v >= umod()) _v -= umod();
            return *this;
        }
        mint& operator-=(const mint& rhs) {
            _v -= rhs._v;
            if (_v >= umod()) _v += umod();
            return *this;
        }
        mint& operator*=(const mint& rhs) {
            unsigned long long z = _v;
            z *= rhs._v;
            _v = (unsigned int)(z % umod());
            return *this;
        }
        mint& operator/=(const mint& rhs) { return *this = *this * rhs.inv(); }
        
        mint operator+() const { return *this; }
        mint operator-() const { return mint() - *this; }
        
        mint pow(long long n) const {
            assert(0 <= n);
            mint x = *this, r = 1;
            while (n) {
                if (n & 1) r *= x;
                x *= x;
                n >>= 1;
            }
            return r;
        }
        mint inv() const {
            if (prime) {
                assert(_v);
                return pow(umod() - 2);
            } else {
                auto eg = internal::inv_gcd(_v, m);
                assert(eg.first == 1);
                return eg.second;
            }
        }
        
        friend mint operator+(const mint& lhs, const mint& rhs) {
            return mint(lhs) += rhs;
        }
        friend mint operator-(const mint& lhs, const mint& rhs) {
            return mint(lhs) -= rhs;
        }
        friend mint operator*(const mint& lhs, const mint& rhs) {
            return mint(lhs) *= rhs;
        }
        friend mint operator/(const mint& lhs, const mint& rhs) {
            return mint(lhs) /= rhs;
        }
        friend bool operator==(const mint& lhs, const mint& rhs) {
            return lhs._v == rhs._v;
        }
        friend bool operator!=(const mint& lhs, const mint& rhs) {
            return lhs._v != rhs._v;
        }
        
    private:
        unsigned int _v;
        static constexpr unsigned int umod() { return m; }
        static constexpr bool prime = internal::is_prime<m>;
    };
    
    template <int id> struct dynamic_modint : internal::modint_base {
        using mint = dynamic_modint;
        
    public:
        static int mod() { return (int)(bt.umod()); }
        static void set_mod(int m) {
            assert(1 <= m);
            bt = internal::barrett(m);
        }
        static mint raw(int v) {
            mint x;
            x._v = v;
            return x;
        }
        
        dynamic_modint() : _v(0) {}
        template <class T, internal::is_signed_int_t<T>* = nullptr>
        dynamic_modint(T v) {
            long long x = (long long)(v % (long long)(mod()));
            if (x < 0) x += mod();
            _v = (unsigned int)(x);
        }
        template <class T, internal::is_unsigned_int_t<T>* = nullptr>
        dynamic_modint(T v) {
            _v = (unsigned int)(v % mod());
        }
        dynamic_modint(bool v) { _v = ((unsigned int)(v) % mod()); }
        
        unsigned int val() const { return _v; }
        
        mint& operator++() {
            _v++;
            if (_v == umod()) _v = 0;
            return *this;
        }
        mint& operator--() {
            if (_v == 0) _v = umod();
            _v--;
            return *this;
        }
        mint operator++(int) {
            mint result = *this;
            ++*this;
            return result;
        }
        mint operator--(int) {
            mint result = *this;
            --*this;
            return result;
        }
        
        mint& operator+=(const mint& rhs) {
            _v += rhs._v;
            if (_v >= umod()) _v -= umod();
            return *this;
        }
        mint& operator-=(const mint& rhs) {
            _v += mod() - rhs._v;
            if (_v >= umod()) _v -= umod();
            return *this;
        }
        mint& operator*=(const mint& rhs) {
            _v = bt.mul(_v, rhs._v);
            return *this;
        }
        mint& operator/=(const mint& rhs) { return *this = *this * rhs.inv(); }
        
        mint operator+() const { return *this; }
        mint operator-() const { return mint() - *this; }
        
        mint pow(long long n) const {
            assert(0 <= n);
            mint x = *this, r = 1;
            while (n) {
                if (n & 1) r *= x;
                x *= x;
                n >>= 1;
            }
            return r;
        }
        mint inv() const {
            auto eg = internal::inv_gcd(_v, mod());
            assert(eg.first == 1);
            return eg.second;
        }
        
        friend mint operator+(const mint& lhs, const mint& rhs) {
            return mint(lhs) += rhs;
        }
        friend mint operator-(const mint& lhs, const mint& rhs) {
            return mint(lhs) -= rhs;
        }
        friend mint operator*(const mint& lhs, const mint& rhs) {
            return mint(lhs) *= rhs;
        }
        friend mint operator/(const mint& lhs, const mint& rhs) {
            return mint(lhs) /= rhs;
        }
        friend bool operator==(const mint& lhs, const mint& rhs) {
            return lhs._v == rhs._v;
        }
        friend bool operator!=(const mint& lhs, const mint& rhs) {
            return lhs._v != rhs._v;
        }
        
    private:
        unsigned int _v;
        static internal::barrett bt;
        static unsigned int umod() { return bt.umod(); }
    };
    template <int id> internal::barrett dynamic_modint<id>::bt = 998244353;
    
    using modint998244353 = static_modint<998244353>;
    using modint1000000007 = static_modint<1000000007>;
    using modint = dynamic_modint<-1>;
    
    namespace internal {
        
        template <class T>
        using is_static_modint = std::is_base_of<internal::static_modint_base, T>;
        
        template <class T>
        using is_static_modint_t = std::enable_if_t<is_static_modint<T>::value>;
        
        template <class> struct is_dynamic_modint : public std::false_type {};
        template <int id>
        struct is_dynamic_modint<dynamic_modint<id>> : public std::true_type {};
        
        template <class T>
        using is_dynamic_modint_t = std::enable_if_t<is_dynamic_modint<T>::value>;
        
    }  // namespace internal
    
}  // namespace atcoder

using mint=atcoder::modint1000000007;

//Union Find

struct UF{
    int n;
    vector<int> par,size;
    
    void init(int n_){
        n=n_;
        par.assign(n,-1);
        size.assign(n,1);
        
        for(int i=0;i<n;i++){
            par[i]=i;
        }
    }
    
    int root(int a){
        if(par[a]==a) return a;
        else return par[a]=root(par[a]);
    }
    
    void unite(int a,int b){
        if(root(a)!=root(b)){
            if(root(a)>root(b)) swap(a,b);
            size[root(a)]+=size[root(b)];
            par[root(b)]=root(a);
        }
    }
    
    bool check(int a,int b){
        return root(a)==root(b);
    }
};

int main(){
    
    std::ifstream in("text.txt");
    std::cin.rdbuf(in.rdbuf());
    cin.tie(0);
    ios::sync_with_stdio(false);
    
    int N,M;cin>>N>>M;
    string S;cin>>S;
    vector<char> A(M);
    vector<int> B(M);
    map<char,int> MA;
    for(int i=0;i<M;i++){
        cin>>A[i]>>B[i];
        MA[A[i]]=i;
    }
    for(int i=0;i<M;i++) A[i]=char('a'+i);
    for(int i=0;i<N;i++){
        if(!('0'<=S[i]&&S[i]<='9')) S[i]=char('a'+MA[S[i]]);
    }
    
    mint ans=0;
    
    for(int bit=0;bit<(1<<M);bit++){
        bool ok=true;
        for(int i=0;i<M;i++){
            if(bit&(1<<i)){
                if(B[i]<=9){
                    ok=false;
                }
            }
        }
        if(!ok) continue;
        vector<int> T,use(20);
        for(char c:S){
            if('0'<=c&&c<='9') T.push_back(20+int(c-'0'));
            else{
                int i=int(c-'a');
                if(bit&(1<<i)){
                    T.push_back(10+i);
                    T.push_back(i);
                    use[i]=true;
                    use[10+i]=true;
                }else{
                    T.push_back(i);
                    use[i]=true;
                }
            }
        }
        
        UF uf;uf.init(30);
        vector<int> fix(20,-1);
        for(int i=0;i<si(T);i++){
            uf.unite(T[i],T[si(T)-1-i]);
        }
        
        for(int i=0;i<20;i++){
            for(int j=0;j<10;j++){
                if(uf.check(i,20+j)){
                    if(fix[i]!=-1) ok=false;
                    fix[i]=j;
                }
            }
            if(i>=10&&use[i]&&fix[i]==0) ok=false;
            if(i<10&&use[i]&&fix[i]>B[i]/10) ok=false;
        }
        
        for(int i=0;i<10;i++) for(int j=i+1;j<10;j++) if(uf.check(20+i,20+j)) ok=false;
        
        if(!ok) continue;
        
        set<int> ro;
        for(int i=0;i<20;i++) if(use[i]) ro.insert(uf.root(i));
        
        for(int Y=bit;;Y=(Y-1)&bit){
            int X=bit-Y;
            vector<int> l(20,-1),r(20,10);
            for(int i=0;i<M;i++){
                if(!use[10+i]) continue;
                int a=uf.root(10+i);
                if(X&(1<<i)){
                    chmax(l[a],1);
                    chmin(r[a],B[i]/10-1);
                }else{
                    chmax(l[a],B[i]/10);
                    chmin(r[a],B[i]/10);
                }
                if(fix[10+i]!=-1){
                    chmax(l[a],fix[10+i]);
                    chmin(r[a],fix[10+i]);
                }
            }
            for(int i=0;i<M;i++){
                int a=uf.root(i);
                if(!(bit&(1<<i))){
                    chmax(l[a],0);
                    chmin(r[a],min(9,B[i]));
                }else if(X&(1<<i)){
                    chmax(l[a],0);
                    chmin(r[a],9);
                }else{
                    chmax(l[a],0);
                    chmin(r[a],B[i]%10);
                }
                if(fix[i]!=-1){
                    chmax(l[a],fix[i]);
                    chmin(r[a],fix[i]);
                }
            }
            
            mint tasu=1;
            
            for(int a:ro){
                if(l[a]>r[a]) tasu=0;
                tasu*=(r[a]-l[a]+1);
            }
            
            ans+=tasu;
            
            if(Y==0) break;
            // Yがギリギリ Xが余裕あり
        }
    }
    
    cout<<ans.val()<<endl;
}
accuracy: 0.813953488372093
time_ms: 10
memory_kb: 3460
final_rank: 19

code_snippet: #include <bits/stdc++.h>
using namespace std;
const long long MOD = 1000000007;
long long dfs(string &s, vector<int> &u, vector<int> &t, int p){
  if (p < 10){
    long long ans = 0;
    t[p] = 0;
    ans += dfs(s, u, t, p + 1);
    if (u[p] >= 20){
      t[p] = 1;
      ans += dfs(s, u, t, p + 1);
    }
    if (u[p] >= 10){
      t[p] = 2;
      ans += dfs(s, u, t, p + 1);
    }
    ans %= MOD;
    return ans;
  } else {
    vector<int> l, r;
    for (int i = 0; i < 10; i++){
      l.push_back(i);
      r.push_back(i);
    }
    int N = 10;
    vector<int> id(10, 0);
    for (int i = 0; i < 10; i++){
      id[i] = N;
      if (t[i] == 0){
        l.push_back(0);
        r.push_back(min(u[i], 9));
        N++; 
      }
      if (t[i] == 1){
        l.push_back(1);
        r.push_back(u[i] / 10 - 1);
        l.push_back(0);
        r.push_back(9);
        N += 2;
      }
      if (t[i] == 2){
        l.push_back(u[i] / 10);
        r.push_back(u[i] / 10);
        l.push_back(0);
        r.push_back(u[i] % 10);
        N += 2;
      }
    }
    int L = s.size();
    vector<int> a;
    for (int i = 0; i < L; i++){
      if (isdigit(s[i])){
        a.push_back(s[i] - '0');
      } else {
        int c = s[i] - 'a';
        a.push_back(id[c]);
        if (t[c] > 0){
          a.push_back(id[c] + 1);
        }
      }
    }
    int M = a.size();
    vector<int> b = a;
    reverse(b.begin(), b.end());
    vector<vector<int>> E(N);
    for (int i = 0; i < M; i++){
      E[a[i]].push_back(b[i]);
      E[b[i]].push_back(a[i]);
    }
    vector<bool> used(N, false);
    long long ans = 1;
    for (int i = 0; i < N; i++){
      if (!used[i]){
        used[i] = true;
        int mn = 0, mx = 9;
        queue<int> Q;
        Q.push(i);
        while (!Q.empty()){
          int v = Q.front();
          Q.pop();
          mn = max(mn, l[v]);
          mx = min(mx, r[v]);
          for (int w : E[v]){
            if (!used[w]){
              used[w] = true;
              Q.push(w);
            }
          }
        }
        ans *= max(mx - mn + 1, 0);
      }
    }
    return ans;
  }
}
int main(){
  int N, m;
  cin >> N >> m;
  string s;
  cin >> s;
  vector<int> u(10, 0);
  for (int i = 0; i < m; i++){
    char c;
    cin >> c;
    cin >> u[c - 'a'];
  }
  vector<int> t(10);
  cout << dfs(s, u, t, 0) << endl;
}
accuracy: 1
time_ms: 930
memory_kb: 3252
final_rank: 15

code_snippet: #include<iostream>
#include<string>
#include<cstdio>
#include<vector>
#include<cmath>
#include<algorithm>
#include<functional>
#include<iomanip>
#include<queue>
#include<ciso646>
#include<random>
#include<map>
#include<set>
#include<bitset>
#include<stack>
#include<unordered_map>
#include<unordered_set>
#include<utility>
#include<cassert>
#include<complex>
#include<numeric>
#include<array>
using namespace std;

//#define int long long
typedef long long ll;

typedef unsigned long long ul;
typedef unsigned int ui;
constexpr ll mod = 1000000007;
const ll INF = mod * mod;
typedef pair<int, int>P;
#define stop char nyaa;cin>>nyaa;
#define rep(i,n) for(int i=0;i<n;i++)
#define per(i,n) for(int i=n-1;i>=0;i--)
#define Rep(i,sta,n) for(int i=sta;i<n;i++)
#define rep1(i,n) for(int i=1;i<=n;i++)
#define per1(i,n) for(int i=n;i>=1;i--)
#define Rep1(i,sta,n) for(int i=sta;i<=n;i++)
#define all(v) (v).begin(),(v).end()
typedef pair<ll, ll> LP;
typedef long double ld;
typedef pair<ld, ld> LDP;
const ld eps = 1e-10;
const ld pi = acosl(-1.0);

ll mod_pow(ll x, ll n, ll m = mod) {
	if (n < 0) {
		ll res = mod_pow(x, -n, m);
		return mod_pow(res, m - 2, m);
	}
	if (abs(x) >= m)x %= m;
	if (x < 0)x += m;
	ll res = 1;
	while (n) {
		if (n & 1)res = res * x % m;
		x = x * x % m; n >>= 1;
	}
	return res;
}
struct modint {
	ll n;
	modint() :n(0) { ; }
	modint(ll m) :n(m) {
		if (n >= mod)n %= mod;
		else if (n < 0)n = (n % mod + mod) % mod;
	}
	operator int() { return n; }
};
bool operator==(modint a, modint b) { return a.n == b.n; }
modint operator+=(modint& a, modint b) { a.n += b.n; if (a.n >= mod)a.n -= mod; return a; }
modint operator-=(modint& a, modint b) { a.n -= b.n; if (a.n < 0)a.n += mod; return a; }
modint operator*=(modint& a, modint b) { a.n = ((ll)a.n * b.n) % mod; return a; }
modint operator+(modint a, modint b) { return a += b; }
modint operator-(modint a, modint b) { return a -= b; }
modint operator*(modint a, modint b) { return a *= b; }
modint operator^(modint a, ll n) {
	if (n == 0)return modint(1);
	modint res = (a * a) ^ (n / 2);
	if (n % 2)res = res * a;
	return res;
}

ll inv(ll a, ll p) {
	return (a == 1 ? 1 : (1 - p * inv(p % a, a)) / a + p);
}
modint operator/(modint a, modint b) { return a * modint(inv(b, mod)); }
modint operator/=(modint& a, modint b) { a = a / b; return a; }
const int max_n = 1 << 21;
modint fact[max_n], factinv[max_n];
void init_f() {
	fact[0] = modint(1);
	for (int i = 0; i < max_n - 1; i++) {
		fact[i + 1] = fact[i] * modint(i + 1);
	}
	factinv[max_n - 1] = modint(1) / fact[max_n - 1];
	for (int i = max_n - 2; i >= 0; i--) {
		factinv[i] = factinv[i + 1] * modint(i + 1);
	}
}
modint comb(int a, int b) {
	if (a < 0 || b < 0 || a < b)return 0;
	return fact[a] * factinv[b] * factinv[a - b];
}
modint combP(int a, int b) {
	if (a < 0 || b < 0 || a < b)return 0;
	return fact[a] * factinv[a - b];
}


void solve() {
	int n, m; cin >> n >> m;
	string s; cin >> s;

	string sc; sc.resize(m);
	vector<int> u(m);
	rep(i, m) {
		cin >> sc[i] >> u[i];
	}
	modint ans = 0;
	rep(i, (1 << m)) {
		vector<int> ad(m);
		int cur = 10;
		rep(j, m) {
			ad[j] = cur;
			if (i&(1<<j)) {
				cur += 2;
			}
			else {
				cur += 1;
			}
		}
		vector<int> v;
		rep(j, s.size()) {
			if ('0' <= s[j] && s[j] <= '9') {
				v.push_back(s[j] - '0');
			}
			else {
				int id = sc.find(s[j]);
				if (i & (1 << id)) {
					v.push_back(ad[id] + 1);
				}
				v.push_back(ad[id]);
			}
		}
		vector<vector<int>> ids;
		vector<bool> used(cur);
		vector<vector<int>> G(cur);
		rep(i, v.size() / 2) {
			G[v[i]].push_back(v[v.size() - 1 - i]);
			G[v[v.size() - 1 - i]].push_back(v[i]);
		}
		rep(j, cur)if (!used[j]) {
			used[j] = true;
			queue<int> q; q.push(j);
			vector<int> cid;
			while (!q.empty()) {
				int v = q.front(); q.pop();
				cid.push_back(v);
				for (int to : G[v])if(!used[to]) {
					used[to] = true; q.push(to);
				}
			}
			ids.push_back(cid);
		}
		//j&(1<<k) のとき、十の位が一致
		rep(j, (1 << m)) {
			vector<int> l(cur), r(cur);
			rep(k, 10) {
				l[k] = k, r[k] = k;
			}
			rep(k, m) {
				if (i & (1 << k)) {
					if (j & (1 << k)) {
						l[ad[k] + 1] = max(1,u[k] / 10);
						r[ad[k] + 1] = u[k] / 10;
						l[ad[k]] = 0;
						r[ad[k]] = u[k] % 10;
					}
					else {
						l[ad[k] + 1] = 1;
						r[ad[k] + 1] = u[k] / 10 - 1;
						l[ad[k]] = 0;
						r[ad[k]] = 9;
					}
				}
				else {
					if (j & (1 << k)) {
						l[ad[k]] = 0;
						r[ad[k]] = min(u[k], 9);
					}
					else {
						l[ad[k]] = 0;
						r[ad[k]] = -1;
					}
				}
			}
			modint num = 1;
			for (vector<int> v : ids) {
				int le = -1, ri = 11;
				for (int id : v) {
					le = max(le, l[id]);
					ri = min(ri, r[id]);
				}
				int cnt = ri - le + 1;
				if (cnt < 0)cnt = 0;
				num *= cnt;
			}
			ans += num;
		}
	}
	cout << ans << "\n";
}

signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	//cout << fixed << setprecision(10);
	//init_f();
	//init();
	//expr();

	//int t; cin >> t; rep(i, t)
	solve();
	return 0;
}
accuracy: 1
time_ms: 270
memory_kb: 36268
final_rank: 17

code_snippet: #include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>

#include <algorithm>
#include <bitset>
#include <complex>
#include <iostream>
#include <map>
#include <numeric>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>

#include <cassert>
#include <functional>

typedef long long ll;
using namespace std;

#ifndef LOCAL
#define debug(...) ;
#else
#define debug(...) cerr << __LINE__ << " : " << #__VA_ARGS__ << " = " << _tostr(__VA_ARGS__) << endl;

template<typename T>
ostream &operator<<(ostream &out, const vector<T> &v);

template<typename T1, typename T2>
ostream &operator<<(ostream &out, const pair<T1, T2> &p) {
  out << "{" << p.first << ", " << p.second << "}";
  return out;
}

template<typename T>
ostream &operator<<(ostream &out, const vector<T> &v) {
  out << '{';
  for (const T &item : v) out << item << ", ";
  out << "\b\b}";
  return out;
}

void _tostr_rec(ostringstream &oss) {
  oss << "\b\b \b";
}

template<typename Head, typename... Tail>
void _tostr_rec(ostringstream &oss, Head &&head, Tail &&... tail) {
  oss << head << ", ";
  _tostr_rec(oss, forward<Tail>(tail)...);
}

template<typename... T>
string _tostr(T &&... args) {
  ostringstream oss;
  int size = sizeof...(args);
  if (size > 1) oss << "{";
  _tostr_rec(oss, forward<T>(args)...);
  if (size > 1) oss << "}";
  return oss.str();
}
#endif

#define mod 1000000007 //1e9+7(prime number)
#define INF 1000000000 //1e9
#define LLINF 2000000000000000000LL //2e18
#define SIZE 2010

/* UnionFind */

struct UnionFind {
  vector<int> data;
  UnionFind(int s): data(s, -1) {}

  int root(int x) {
    if (data[x] < 0) return x;
    return data[x] = root(data[x]);
  }

  bool set(int x, int y) {
    x = root(x);
    y = root(y);
    if (x == y) return false;
    if (data[x] > data[y]) swap(x, y);
    data[x] += data[y];
    data[y] = x;
    return true;
  }

  bool check(int x, int y) {
    return root(x) == root(y);
  }

  int size(int x) {
    return -data[root(x)];
  }
};

int u[256];

int main() {
  int N, M, SI[SIZE];
  char S[SIZE], var[SIZE];
  int order[SIZE] = {};

  scanf("%d%d", &N, &M);
  scanf("%s", S);

  for (int i = 0; i < M; i++) {
    char _var;
    int _u;
    scanf(" %c%d", &_var, &_u);
    u[i] = _u;
    order[_var] = i;
    var[i] = i;
    debug(_var, i);
  }

  ll ans = 0;

  for (int i = 0; i < (1 << M); i++) { //2桁にする文字
    vector<int> vec;
    set<int> ss;

    for (int j = 0; j < N; j++) {
      if (isdigit(S[j])) {
        vec.push_back(100 + (S[j] - '0'));
      } else {
        int o = order[S[j]];
        if ((1 << o) & i) vec.push_back(o * 2 + 1);
        vec.push_back(o * 2);
      }
    }

    debug(vec);

    UnionFind uf(150);

    for (int i = 0; i < vec.size(); i++) {
      ss.insert(vec[i]);
      uf.set(vec[i], vec[(int)vec.size() - i - 1]);
    }

    vector<vector<int>> groups(150);

    for (auto s : ss) {
      groups[uf.root(s)].push_back(s);
    }

    for (int j = 0; j < (1 << M); j++) { //10の位が条件と同じ
      if ((i & j) != j) continue;

      ll subAns = 1;

      for (auto s : ss) {
        if (groups[s].empty()) continue;
        int L = 0, R = 9; // [L, R]
        for (auto v : groups[s]) {
          if (v >= 100) {
            L = max(L, v - 100);
            R = min(R, v - 100);
          } else if (v % 2) { //10の位
            int d = u[v / 2] / 10;
            if (j & (1 << (v / 2))) {
              L = max({L, 1, d});
              R = min(R, d);
            } else {
              debug(d);
              L = max(L, 1);
              R = min(R, d - 1);
            }
          } else { //1の位
            if (j & (1 << (v / 2))) {
              int d = u[v / 2] % 10;
              R = min(R, d);
            } else {
              // 0-9 OK
              R = min(R, u[v / 2]);
            }
          }
        }
        debug(groups[s], R, L);
        subAns *= max(0, R - L + 1);
        subAns %= mod;
      }

      debug(i, j, subAns);

      ans = (ans + subAns) % mod;
    }
  }

  cout << ans << endl;

  return 0;
}
accuracy: 1
time_ms: 20
memory_kb: 3264
final_rank: 4

code_snippet: #include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<vector>
#include<cmath>
#include<algorithm>
#include<map>
#include<queue>
#include<deque>
#include<iomanip>
#include<tuple>
#include<cassert>
#include<set>
using namespace std;
typedef long long int LL;
typedef pair<int,int> P;
typedef pair<LL,int> LP;
const int INF=1<<30;
const LL MAX=1e9+7;

void array_show(int *array,int array_n,char middle=' '){
    for(int i=0;i<array_n;i++)printf("%d%c",array[i],(i!=array_n-1?middle:'\n'));
}
void array_show(LL *array,int array_n,char middle=' '){
    for(int i=0;i<array_n;i++)printf("%lld%c",array[i],(i!=array_n-1?middle:'\n'));
}
void array_show(vector<int> &vec_s,int vec_n=-1,char middle=' '){
    if(vec_n==-1)vec_n=vec_s.size();
    for(int i=0;i<vec_n;i++)printf("%d%c",vec_s[i],(i!=vec_n-1?middle:'\n'));
}
void array_show(vector<LL> &vec_s,int vec_n=-1,char middle=' '){
    if(vec_n==-1)vec_n=vec_s.size();
    for(int i=0;i<vec_n;i++)printf("%lld%c",vec_s[i],(i!=vec_n-1?middle:'\n'));
}

class union_find_tree{
private:
    static constexpr int uft_N=100005;
    int uft_n;
    queue<int> uft_q1;
    vector<int> uft_parent;
    vector<int> uft_num;
public:
    void init(){
        uft_parent.assign(uft_n,-1);
        uft_num.assign(uft_n,1);
    }
    union_find_tree(int uft_n_init){
        assert(uft_n_init>=0);
        uft_n=uft_n_init;
        init();
    }
    union_find_tree(){
        uft_n=uft_N;
        init();
    }

    int check_parent(int uft_x){
        assert(uft_x>=0 && uft_x<uft_n);
        if(uft_parent[uft_x]!=-1){
            uft_q1.push(uft_x);
            return check_parent(uft_parent[uft_x]);
        }
        int uft_a;
        while(!uft_q1.empty()){
            uft_a=uft_q1.front(),uft_q1.pop();
            uft_parent[uft_a]=uft_x;
        }
        return uft_x;
    }

    bool connect(int uft_x,int uft_y){
        assert(uft_x>=0 && uft_x<uft_n);
        assert(uft_y>=0 && uft_y<uft_n);
        uft_x=check_parent(uft_x),uft_y=check_parent(uft_y);
        if(uft_x==uft_y)return true;
        if(uft_num[uft_x]>uft_num[uft_y])swap(uft_x,uft_y);
        uft_parent[uft_x]=uft_y;
        uft_num[uft_y]+=uft_num[uft_x];
        return false;
    }

    int size(int pos){
        pos=check_parent(pos);
        return uft_num[pos];
    }
};

int main(){
    int n,m;
    int i,j,k;
    LL a,b,c;
    LL s=0;
    char ca;
    string sa;
    vector<pair<char,int>> v1;
    vector<int> va;
    int t[12];
    int num[12];
    int able[50][2];
    cin>>n>>m;
    cin>>sa;
    for(i=0;i<m;i++){
        cin>>ca>>a;
        v1.push_back(make_pair(ca,a));
        b=ca-'a';
        t[b]=i;
    }
    for(i=0;i<(1<<m);i++){
        union_find_tree ua(40);
        va.clear();
        for(j=0;j<m;j++){
            if((i&(1<<j)) && v1[j].second<10)break;
        }
        if(j<m)continue;
        for(j=0;j<n;j++){
            a=sa[j]-'0';
            if(0<=a && a<=9)va.push_back(a);
            else{
                a=sa[j]-'a';
                b=t[a];
                a=t[a]*2+10;
                if(i&(1<<b))va.push_back(a+1);
                va.push_back(a);
            }
        }
        //array_show(va);
        a=va.size();
        for(j=0;j<a;j++){
            if(a-j-1<=j)break;
            ua.connect(va[j],va[a-j-1]);
        }
        for(j=0;j<(1<<m);j++){
            for(k=0;k<50;k++){
                able[k][0]=-1,able[k][1]=INF;
            }
            for(k=0;k<m;k++){
                if((i&(1<<k))==0 && j&(1<<k))j+=1<<k;
            }
            if(j>=(1<<m))break;
            for(k=0;k<m;k++){
                if(i&(1<<k)){
                    if(j&(1<<k)){
                        a=ua.check_parent(k*2+10+1);
                        able[a][0]=max(able[a][0],v1[k].second/10);
                        able[a][1]=min(able[a][1],v1[k].second/10);
                        a=ua.check_parent(k*2+10);
                        able[a][0]=max(able[a][0],0);
                        able[a][1]=min(able[a][1],v1[k].second%10);
                    }else{
                        a=ua.check_parent(k*2+10+1);
                        able[a][0]=max(able[a][0],1);
                        able[a][1]=min(able[a][1],v1[k].second/10-1);
                        a=ua.check_parent(k*2+10);
                        able[a][0]=max(able[a][0],0);
                        able[a][1]=min(able[a][1],9);
                    }
                }else{
                    a=ua.check_parent(k*2+10);
                    able[a][0]=max(able[a][0],0);
                    able[a][1]=min({able[a][1],v1[k].second,9});
                }
            }
            for(k=0;k<10;k++){
                a=ua.check_parent(k);
                able[a][0]=max(able[a][0],k);
                able[a][1]=min(able[a][1],k);
            }
            a=1;
            for(k=0;k<50;k++){
                if(able[k][0]==-1)continue;
                a*=max(able[k][1]-able[k][0]+1,0);
                if(a>=MAX)a%=MAX;
                //cout<<able[k][0]<<" "<<able[k][1]<<endl;
            }
            //cout<<i<<" "<<j<<" "<<a<<endl;
            s+=a;
            s%=MAX;
        }
    }
    cout<<s<<endl;
}
accuracy: 1
time_ms: 20
memory_kb: 3252
final_rank: 3

code_snippet: #include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll, ll> l_l;
typedef pair<int, int> i_i;
template<class T>
inline bool chmax(T &a, T b) {
    if(a < b) {
        a = b;
        return true;
    }
    return false;
}

template<class T>
inline bool chmin(T &a, T b) {
    if(a > b) {
        a = b;
        return true;
    }
    return false;
}

struct UnionFind {
    vector<int> par;
    vector<int> rank;
    vector<ll> state;
    UnionFind(int n = 1) {
        init(n);
    }

    void init(int n = 1) {
        par.resize(n + 1); rank.resize(n + 1); state.resize(n + 1);
        for (int i = 0; i <= n; ++i) par[i] = i, rank[i] = 0, state[i] = 0;
    }

    int root(int x) {
        if (par[x] == x) {
            return x;
        }
        else {
            int r = root(par[x]);
            return par[x] = r;
        }
    }

    bool issame(int x, int y) {
        return root(x) == root(y);
    }

    bool merge(int x, int y) {
        x = root(x); y = root(y);
        if (x == y) return false;
        if (rank[x] < rank[y]) swap(x, y);
        if (rank[x] == rank[y]) ++rank[x];
        par[y] = x;
        state[x] &= state[y];
        return true;
    }
};

const long double EPS = 1e-10;
const long long INF = 1e18;
const long double PI = acos(-1.0L);
const ll mod = 1000000007;
ll N, M;
string S;
char var[10];
int u[10];
map<char, int> mp;
vector<int> v;
ll ans = 0;

void f() {
    UnionFind uni(30);
    for(int i = 0; i < 10; i++) {
        uni.state[i] = (1 << i);
    }
    for(int i = 0; i < M; i++) {
        if(v[i] == 0) {
            for(int j = 0; j <= min(9, u[i]); j++) {
                uni.state[10+i] |= (1 << j);
            }
            uni.state[20+i] = 1;
        } else if(v[i] == 1) {
            uni.state[10+i] |= (1 << (u[i]/10));
            for(int j = 0; j <= (u[i] % 10); j++) {
                uni.state[20+i] |= (1 << j);
            }
        } else {
            for(int j = 1; j < (u[i] / 10); j++) {
                uni.state[10+i] |= (1 << j);
            }
            for(int j = 0; j <= 9; j++) {
                uni.state[20+i] |= (1 << j);
            }
        }
    }
    vector<int> A;
    //cerr << S << endl;
    for(auto c : S) {
        if('0' <= c and c <= '9') {
            A.push_back((int)(c - '0'));
        } else {
            int idx = mp[c];
            if(v[idx] == 0) {
                A.push_back(10 + idx);
            } else {
                A.push_back(10+idx);
                A.push_back(20+idx);
            }
        }
    }
    auto B = A;
    reverse(B.begin(), B.end());
    for(int i = 0; i < A.size(); i++) {
        //cerr << A[i] << " " << B[i] << endl;
        uni.merge(A[i], B[i]);
    }
    //cerr << endl;
    ll tmp = 1;
    for(int i = 0; i < 10; i++) {
        if(uni.root(i) != i) continue;
        tmp *= __builtin_popcount(uni.state[i]);
        tmp %= mod;
    }
    for(int a = 0; a < M; a++) {
        for(int j = 1; j <= 2; j++) {
            int i = 10 * j + a;
            if(uni.root(i) != i) continue;
            tmp *= __builtin_popcount(uni.state[i]);
            tmp %= mod;
        }
    }
    ans += tmp;
    ans %= mod;
}

void dfs() {
    if(v.size() == M) {
        f();
        return;
    }
    int idx = v.size();
    v.push_back(0);
    dfs();
    v.pop_back();
    v.push_back(1);
    if(u[idx] >= 10) dfs();
    v.pop_back();
    v.push_back(2);
    if(u[idx] >= 20) dfs();
    v.pop_back();
}

int main() {
    cin >> N >> M;
    cin >> S;
    for(int i = 0; i < M; i++) {
        cin >> var[i] >> u[i];
        mp[var[i]] = i;
    }
    dfs();
    cout << ans << endl;
    return 0;
}
accuracy: 1
time_ms: 550
memory_kb: 3232
final_rank: 12

code_snippet: #include<iostream>
#include<vector>
#include<map>
using namespace std;
#include<vector>
struct UF{
	int n;
	vector<int>parent,rank;
	UF(int n_=0):n(n_),parent(n_),rank(n_,1)
	{
		for(int i=0;i<n_;i++)parent[i]=i;
	}
	int find(int a){return parent[a]!=a?parent[a]=find(parent[a]):a;}
	bool same(int a,int b){return find(a)==find(b);}
	bool unite(int a,int b)
	{
		a=find(a),b=find(b);
		if(a==b)return false;
		if(rank[a]<rank[b])
		{
			parent[a]=b;
			rank[b]+=rank[a];
		}
		else
		{
			parent[b]=a;
			rank[a]+=rank[b];
		}
		return true;
	}
	int size(int a){return rank[find(a)];}
};
const long mod=1e9+7;
int N,M;
string s;
char var[10];
int u[10];
main()
{
	cin>>N>>M;
	cin>>s;
	for(int i=0;i<M;i++)
	{
		cin>>var[i]>>u[i];
	}
	long ans=0;
	for(int i=0;i<1<<M;i++)
	{
		for(int j=i;;j=j-1&i)
		{
			map<char,vector<int> >now;
			vector<int>L,R;
			for(char c='0';c<='9';c++)
			{
				now[c]=(vector<int>){(int)L.size()};
				L.push_back(c-'0');
				R.push_back(c-'0');
			}
			bool ok=true;
			for(int k=0;k<M;k++)
			{
				int id=L.size();
				if(i>>k&1)
				{
					if(j>>k&1)
					{
						if(u[k]<10)
						{
							ok=false;
							break;
						}
						now[var[k]]=(vector<int>){id,id+1};
						L.push_back(u[k]/10);
						R.push_back(u[k]/10);
						L.push_back(0);
						R.push_back(u[k]%10);
					}
					else
					{
						if(u[k]<20)
						{
							ok=false;
							break;
						}
						now[var[k]]=(vector<int>){id,id+1};
						L.push_back(1);
						R.push_back(u[k]/10-1);
						L.push_back(0);
						R.push_back(9);
					}
				}
				else
				{
					now[var[k]]=(vector<int>){id};
					L.push_back(0);
					R.push_back(u[k]<9?u[k]:9);
				}
			}
			if(ok)
			{
				vector<int>S;
				for(char c:s)for(int id:now[c])S.push_back(id);
				UF uf(L.size());
				for(int k=0;k<S.size();k++)
				{
					uf.unite(S[k],S[S.size()-k-1]);
				}
				long ret=1;
				vector<bool>vis(L.size(),false);
				for(int k=0;k<L.size();k++)if(!vis[uf.find(k)])
				{
					vis[uf.find(k)]=true;
					int l=0,r=9;
					for(int m=0;m<L.size();m++)if(uf.find(m)==uf.find(k))
					{
						if(l<L[m])l=L[m];
						if(r>R[m])r=R[m];
					}
					if(l>r)ret=0;
					else ret=ret*(r-l+1)%mod;
				}
				(ans+=ret)%=mod;
			}
			if(j==0)break;
		}
	}
	cout<<ans<<endl;
}
accuracy: 1
time_ms: 750
memory_kb: 3220
final_rank: 14

code_snippet: #include <bits/stdc++.h>
using namespace std;

using ll = long long;
using pii = pair<int, int>;

constexpr int mod = 1e9 + 7;

class union_find {
public:
    union_find(int n) : par(n, -1) {}

    int root(int x) {
        return par[x] < 0 ? x : par[x] = root(par[x]);
    }

    void unite(int x, int y) {
        x = root(x), y = root(y);
        if(x == y) return;
        if(par[x] < par[y]) swap(x, y);
        par[x] += par[y];
        par[y] = x;
    }

private:
    vector<int> par;
};

int main() {
    int n, m; cin >> n >> m;
    string s; cin >> s;
    vector<char> var(m);
    vector<int> u(m);
    for(int i = 0; i < m; ++i) {
        cin >> var[i] >> u[i];
    }

    ll ans = 0;
    for(int S1 = 0; S1 < (1 << m); ++S1) { // determine 1 digit or 2 digits
        {
            bool check = true;
            for(int i = 0; i < m; ++i) {
                check &= !(S1 & (1 << i)) || (u[i] >= 10);
            }
            if(!check) continue;
        }
        vector<pii> vs;
        for(int i = 0; i < n; ++i) {
            if(isdigit(s[i])) {
                vs.emplace_back(-1, s[i] - '0');
            } else {
                const int id = find(begin(var), end(var), s[i]) - begin(var);
                if(S1 & (1 << id)) {
                    vs.emplace_back(id, 2);
                }
                vs.emplace_back(id, 1);
            }
        }

        // integrate
        auto ss = vs;
        sort(begin(ss), end(ss));
        ss.erase(unique(begin(ss), end(ss)), end(ss));
        const int vsz = vs.size();
        const int sz = ss.size();
        union_find uf(sz);
        for(int i = 0; i < vsz; ++i) {
            const int id1 = lower_bound(begin(ss), end(ss), vs[i]) - begin(ss);
            const int id2 = lower_bound(begin(ss), end(ss), vs[vsz - i - 1]) - begin(ss);
            uf.unite(id1, id2);
        }

        // determine 2nd digit
        for(int S2 = 0; S2 < (1 << m); ++S2) {
            {
                bool check = (S1 & S2) == S2;
                for(int i = 0; i < m; ++i) {
                    check &= !(S2 & (1 << i)) || u[i] >= 10;
                }
                if(!check) continue;
            }
            vector<ll> lb(sz, 0), ub(sz, mod);
            for(int i = 0; i < sz; ++i) {
                const int id = uf.root(i);
                const int j = ss[i].first;
                ub[id] = min(ub[id], 9LL);
                if(j == -1) {
                    lb[id] = max(lb[id], (ll)ss[i].second);
                    ub[id] = min(ub[id], (ll)ss[i].second);
                } else if(ss[i].second == 2) {
                    lb[id] = max(lb[id], 1LL);
                    if(S2 & (1 << j)) {
                        lb[id] = max(lb[id], (ll)(u[j] / 10));
                        ub[id] = min(ub[id], (ll)(u[j] / 10));
                    } else {
                        ub[id] = min(ub[id], (ll)(u[j] / 10 - 1));
                    }
                } else {
                    if(S2 & (1 << j) || u[j] < 10) {
                        ub[id] = min(ub[id], (ll)(u[j] % 10));
                    }
                }
            }

            ll tans = 1;
            for(int i = 0; i < sz; ++i) {
                if(ub[i] == mod) continue;
                (tans *= max(0LL, ub[i] - lb[i] + 1)) %= mod;
            }
            (ans += tans) %= mod;
        }
    }

    cout << ans << endl;
}
accuracy: 1
time_ms: 90
memory_kb: 3216
final_rank: 7