zoukankan      html  css  js  c++  java
  • AC自动机部分题目

    模板题:

    Keywords Search

    View Code
    //#pragma comment(linker,"/STACK:327680000,327680000")
    #include <iostream>
    #include <cstdio>
    #include <cmath>
    #include <vector>
    #include <cstring>
    #include <algorithm>
    #include <string>
    #include <set>
    #include <functional>
    #include <numeric>
    #include <sstream>
    #include <stack>
    #include <map>
    #include <queue>
    
    #define CL(arr, val)    memset(arr, val, sizeof(arr))
    #define REP(i, n)       for((i) = 0; (i) < (n); ++(i))
    #define FOR(i, l, h)    for((i) = (l); (i) <= (h); ++(i))
    #define FORD(i, h, l)   for((i) = (h); (i) >= (l); --(i))
    #define L(x)    (x) << 1
    #define R(x)    (x) << 1 | 1
    #define MID(l, r)   (l + r) >> 1
    #define Min(x, y)   (x) < (y) ? (x) : (y)
    #define Max(x, y)   (x) < (y) ? (y) : (x)
    #define E(x)        (1 << (x))
    #define iabs(x)     (x) < 0 ? -(x) : (x)
    #define OUT(x)  printf("%I64d\n", x)
    #define Read()  freopen("data.in", "r", stdin)
    #define Write() freopen("data.out", "w", stdout);
    
    typedef long long LL;
    const double eps = 1e-8;
    const double pi = acos(-1.0);
    const double inf = ~0u>>2;
    
    
    using namespace std;
    
    const int N = 10010;
    
    struct Node {
        Node* fail;
        Node* next[26];
        int cnt;
        Node() {
            CL(next, NULL);
            fail = NULL;
            cnt = 0;
        }
    };
    
    class AC_automaton : public Node {
    public:
        Node* root;
        int head, tail;
    
        void init() {root = new Node(); head = tail = 0;}
    
        void Insert(char* );
        void Build();
        int Search(char* );
    }AC;
    
    void AC_automaton::Insert(char* st) {
        Node* p = root;
        while(*st) {
            if(p->next[*st - 'a'] == NULL) {
                p->next[*st - 'a'] = new Node();
            }
            p = p->next[*st - 'a'];
            st++;
        }
        p->cnt++;
    }
    
    void AC_automaton::Build() {
        root->fail = NULL;
        deque<Node* > q;
        q.push_back(root);
    
        while(!q.empty()) {
            Node* tmp = q.front();
            Node* p = NULL;
            q.pop_front();
    
            for(int i = 0; i < 26; ++i) {
                if(tmp->next[i] != NULL) {
                    if(tmp == root) tmp->next[i]->fail = root;
                    else {
                        p = tmp->fail;
                        while(p != NULL) {
                            if(p->next[i] != NULL) {
                                tmp->next[i]->fail = p->next[i];
                                break;
                            }
                            p = p->fail;
                        }
                        if(p == NULL)   tmp->next[i]->fail = root;
                    }
                    q.push_back(tmp->next[i]);
                }
            }
        }
    }
    
    int AC_automaton::Search(char* st) {
        int res = 0, t;
        Node* p = root;
        while(*st) {
            t = *st - 'a';
            while(p->next[t] == NULL && p != root)    {p = p->fail;}
    
            p = (p->next[t] != NULL) ? p->next[t] : root;
    
            Node* tmp = p;
            while(tmp != root && tmp->cnt != -1) {
                res += tmp->cnt;
                tmp->cnt = -1;
                tmp = tmp->fail;
            }
            st++;
        }
        return res;
    }
    
    char ss[1000010];
    
    int main() {
        //Read();
    
        int T, n;
        scanf("%d", &T);
        while(T--) {
            AC.init();
            scanf("%d", &n);
            while(n--)  {scanf("%s", ss); AC.Insert(ss);}
            AC.Build();
            scanf("%s", ss);
            printf("%d\n", AC.Search(ss));
        }
        return 0;
    }

    病毒侵袭

    View Code
    //#pragma comment(linker,"/STACK:327680000,327680000")
    #include <iostream>
    #include <cstdio>
    #include <cmath>
    #include <vector>
    #include <cstring>
    #include <algorithm>
    #include <string>
    #include <set>
    #include <functional>
    #include <numeric>
    #include <sstream>
    #include <stack>
    #include <map>
    #include <queue>
    
    #define CL(arr, val)    memset(arr, val, sizeof(arr))
    #define REP(i, n)       for((i) = 0; (i) < (n); ++(i))
    #define FOR(i, l, h)    for((i) = (l); (i) <= (h); ++(i))
    #define FORD(i, h, l)   for((i) = (h); (i) >= (l); --(i))
    #define L(x)    (x) << 1
    #define R(x)    (x) << 1 | 1
    #define MID(l, r)   (l + r) >> 1
    #define Min(x, y)   (x) < (y) ? (x) : (y)
    #define Max(x, y)   (x) < (y) ? (y) : (x)
    #define E(x)        (1 << (x))
    #define iabs(x)     (x) < 0 ? -(x) : (x)
    #define OUT(x)  printf("%I64d\n", x)
    #define Read()  freopen("data.in", "r", stdin)
    #define Write() freopen("data.out", "w", stdout);
    
    typedef long long LL;
    const double eps = 1e-8;
    const double pi = acos(-1.0);
    const double inf = ~0u>>2;
    
    
    using namespace std;
    
    const int N = 10010;
    
    struct Node {
        Node* fail;
        Node* next[130];
        int cnt;
        Node() {
            CL(next, NULL);
            fail = NULL;
            cnt = 0;
        }
    };
    
    int ans[N], tt;
    bool vis[N];
    
    class AC_automaton : public Node {
    public:
        Node* root;
        int head, tail;
    
        void init() {root = new Node(); head = tail = 0;}
    
        void Insert(char*, int );
        void Build();
        int Search(char* );
    }AC;
    
    void AC_automaton::Insert(char* st, int num) {
        Node* p = root;
        while(*st) {
            int t = *st;
            if(p->next[t] == NULL) {
                p->next[t] = new Node();
            }
            p = p->next[t];
            st++;
        }
        p->cnt = num;
    }
    
    void AC_automaton::Build() {
        root->fail = NULL;
        deque<Node* > q;
        q.push_back(root);
    
        while(!q.empty()) {
            Node* tmp = q.front();
            Node* p = NULL;
            q.pop_front();
    
            for(int i = 0; i < 130; ++i) {
                if(tmp->next[i] != NULL) {
                    if(tmp == root) tmp->next[i]->fail = root;
                    else {
                        p = tmp->fail;
                        while(p != NULL) {
                            if(p->next[i] != NULL) {
                                tmp->next[i]->fail = p->next[i];
                                break;
                            }
                            p = p->fail;
                        }
                        if(p == NULL)   tmp->next[i]->fail = root;
                    }
                    q.push_back(tmp->next[i]);
                }
            }
        }
    }
    
    int AC_automaton::Search(char* st) {
        int res = 0, t;
        Node* p = root;
        while(*st) {
            t = *st;
            while(p->next[t] == NULL && p != root)    {p = p->fail;}
    
            p = (p->next[t] != NULL) ? p->next[t] : root;
    
            Node* tmp = p;
            while(tmp != root && tmp->cnt) {
               ans[tt++] = tmp->cnt;
               // vis[tmp->cnt] = true;
                tmp = tmp->fail;
            }
            st++;
        }
        return res;
    }
    
    char ss[10010];
    
    int main() {
        //Read();
    
        int N, M, i, j;
        int tol;
        while(~scanf("%d", &N)) {
            AC.init();
            for(i = 1; i <= N; ++i) {
                scanf("%s", ss);
                AC.Insert(ss, i);
            }
            AC.Build();
            tol = 0;
            scanf("%d", &M);
            for(i = 1; i <= M; ++i) {
                scanf("%s", ss);
                //CL(vis, false);
                tt = 0;
    
                AC.Search(ss);
                if(tt == 0) continue;
                sort(ans, ans + tt);
                printf("web %d:", i);
                for(j = 0; j < tt; ++j) {
                    printf(" %d", ans[j]);
                }
                puts("");
                tol++;
            }
            printf("total: %d\n", tol);
        }
        return 0;
    }

    病毒侵袭持续中

    View Code
    //#pragma comment(linker,"/STACK:327680000,327680000")
    #include <iostream>
    #include <cstdio>
    #include <cmath>
    #include <vector>
    #include <cstring>
    #include <algorithm>
    #include <string>
    #include <set>
    #include <functional>
    #include <numeric>
    #include <sstream>
    #include <stack>
    #include <map>
    #include <queue>
    
    #define CL(arr, val)    memset(arr, val, sizeof(arr))
    #define REP(i, n)       for((i) = 0; (i) < (n); ++(i))
    #define FOR(i, l, h)    for((i) = (l); (i) <= (h); ++(i))
    #define FORD(i, h, l)   for((i) = (h); (i) >= (l); --(i))
    #define L(x)    (x) << 1
    #define R(x)    (x) << 1 | 1
    #define MID(l, r)   (l + r) >> 1
    #define Min(x, y)   (x) < (y) ? (x) : (y)
    #define Max(x, y)   (x) < (y) ? (y) : (x)
    #define E(x)        (1 << (x))
    #define iabs(x)     (x) < 0 ? -(x) : (x)
    #define OUT(x)  printf("%I64d\n", x)
    #define Read()  freopen("data.in", "r", stdin)
    #define Write() freopen("data.out", "w", stdout);
    
    typedef long long LL;
    const double eps = 1e-8;
    const double pi = acos(-1.0);
    const double inf = ~0u>>2;
    
    
    using namespace std;
    
    const int maxn = 1024;
    
    struct Node {
        Node* fail;
        Node* next[26];
        int cnt;
        Node() {
            CL(next, NULL);
            fail = NULL;
            cnt = 0;
        }
    };
    
    int ans[maxn];
    
    class AC_automaton : public Node {
    public:
        Node* root;
    
        void init() {root = new Node();}
    
        void Insert(char*, int );
        void Build();
        int Search(char* );
        void DEL(Node* p) {
            for(int i = 0; i < 26; ++i)
                if(p->next[i] != NULL)  DEL(p->next[i]);
            delete p;
        }
        void Clear() {
            DEL(root);
        }
    }AC;
    
    void AC_automaton::Insert(char* st, int num) {
        Node* p = root;
        while(*st) {
            int t = *st - 'A';
            if(p->next[t] == NULL) {
                p->next[t] = new Node();
            }
            p = p->next[t];
            st++;
        }
        p->cnt = num;
    }
    
    void AC_automaton::Build() {
        root->fail = NULL;
        deque<Node* > q;
        q.push_back(root);
    
        while(!q.empty()) {
            Node* tmp = q.front();
            Node* p = NULL;
            q.pop_front();
    
            for(int i = 0; i < 26; ++i) {
                if(tmp->next[i] != NULL) {
                    if(tmp == root) tmp->next[i]->fail = root;
                    else {
                        p = tmp->fail;
                        while(p != NULL) {
                            if(p->next[i] != NULL) {
                                tmp->next[i]->fail = p->next[i];
                                break;
                            }
                            p = p->fail;
                        }
                        if(p == NULL)   tmp->next[i]->fail = root;
                    }
                    q.push_back(tmp->next[i]);
                }
            }
        }
    }
    
    int AC_automaton::Search(char* st) {
        int res = 0, t;
        Node* p = root;
        while(*st) {
            if(!(*st >= 'A' && *st <= 'Z'))   {++st; p = root; continue ;}
            t = *st - 'A';
            while(p->next[t] == NULL && p != root)    {p = p->fail;}
    
            p = (p->next[t] != NULL) ? p->next[t] : root;
    
            Node* tmp = p;
            while(tmp != root && tmp->cnt) {
                ans[tmp->cnt]++;
                tmp = tmp->fail;
            }
            st++;
        }
        return res;
    }
    
    char tmp[1100][55];
    char ss[2000010];
    
    int main() {
        //Read();
    
        int n, i;
        while(~scanf("%d\n", &n)) {
            AC.init();
            for(i = 1; i <= n; ++i) {
                gets(tmp[i]);
                AC.Insert(tmp[i], i);
            }
            AC.Build();
            gets(ss);
            CL(ans, 0);
            AC.Search(ss);
            for(i = 1; i <= n; ++i) {
                if(ans[i]) {
                    printf("%s: %d\n", tmp[i], ans[i]);
                }
            }
            AC.Clear();
        }
        return 0;
    }

    AC自动机+矩阵 

    DNA Sequence

    问你长度为N的串中不包含了模式串的串有几个

    较为详细的讲解:http://hi.baidu.com/ccsu_010/item/7847a3c17f6fe2bc0d0a7b89

    View Code
    //#pragma comment(linker,"/STACK:327680000,327680000")
    #include <iostream>
    #include <cstdio>
    #include <cmath>
    #include <vector>
    #include <cstring>
    #include <algorithm>
    #include <string>
    #include <set>
    #include <functional>
    #include <numeric>
    #include <sstream>
    #include <stack>
    #include <map>
    #include <queue>
    
    #define CL(arr, val)    memset(arr, val, sizeof(arr))
    #define REP(i, n)       for((i) = 0; (i) < (n); ++(i))
    #define FOR(i, l, h)    for((i) = (l); (i) <= (h); ++(i))
    #define FORD(i, h, l)   for((i) = (h); (i) >= (l); --(i))
    #define L(x)    (x) << 1
    #define R(x)    (x) << 1 | 1
    #define MID(l, r)   (l + r) >> 1
    #define Min(x, y)   (x) < (y) ? (x) : (y)
    #define Max(x, y)   (x) < (y) ? (y) : (x)
    #define E(x)        (1 << (x))
    #define iabs(x)     (x) < 0 ? -(x) : (x)
    #define OUT(x)  printf("%I64d\n", x)
    #define Read()  freopen("data.in", "r", stdin)
    #define Write() freopen("data.out", "w", stdout);
    
    typedef long long LL;
    const double eps = 1e-8;
    const double pi = acos(-1.0);
    const double inf = ~0u>>2;
    
    
    using namespace std;
    
    const int MOD = 100000;
    
    struct Node {
        int fail;
        int next[4];
        bool end;
        void Init() {
            CL(next, -1);
            fail = -1;
            end = 0;
        }
    };
    
    Node node[110];
    int q[110];
    int node_num;
    
    int get(char c) {
        switch (c) {
            case 'A' : return 0;
            case 'G' : return 1;
            case 'C' : return 2;
            case 'T' : return 3;
            default  : return -1;
        }
    }
    
    
    class AC_automaton {
    public:
        int head, tail;
        void Init() {
            for(int i = 0; i < 110; ++i)    node[i].Init();
            node_num = 1;
            head = tail = 0;
        }
        void Insert(char* st) {
            int t, p = 0;
            for( ; *st; ++st) {
                if(node[p].end) break;
    
                t = get(*st);
                if(node[p].next[t] == -1) {
                    node[p].next[t] = node_num++;
                }
                p = node[p].next[t];
            }
            node[p].end = true;
        }
    
        void Build() {
            q[tail++] = 0;
    
            while(head < tail) {
                int tmp = q[head++];
                int p;
                for(int i = 0; i < 4; ++i) {
                    if(node[tmp].next[i] != -1) {
                        if(tmp == 0)    node[node[tmp].next[i]].fail = 0;
                        else {
                            p = node[tmp].fail;
    
                            while(p != -1) {
                                if(node[p].next[i] != -1) {
                                    node[node[tmp].next[i]].fail = node[p].next[i];
                                    if(node[node[p].next[i]].end)   node[node[tmp].next[i]].end = 1;
                                    break;
                                }
                                p = node[p].fail;
                            }
                            if(p == -1) node[node[tmp].next[i]].fail = 0;
                        }
                        q[tail++] = node[tmp].next[i];
                    }
                }
            }
        }
    }AC;
    
    char ss[15];
    
    struct Mat {
        LL mat[110][110];
    }g;
    
    Mat operator * (Mat a, Mat b) {
        Mat c;
        memset(c.mat, 0, sizeof(c.mat));
        int i, j, k;
        for(k = 0; k < node_num; ++k) {
            for(i = 0; i < node_num; ++i) {
                if(a.mat[i][k] <= 0)  continue;
                for(j = 0; j < node_num; ++j) {
                    if(b.mat[k][j] <= 0)    continue;    //剪枝
                    c.mat[i][j] += a.mat[i][k] * b.mat[k][j];
                    if(c.mat[i][j] >= MOD)   c.mat[i][j] %= MOD;
                }
            }
        }
        return c;
    }
    
    Mat operator ^ (Mat a, int k) {
        Mat c;
        int i, j;
        for(i = 0; i < node_num; ++i)
            for(j = 0; j < node_num; ++j)
                c.mat[i][j] = (i == j);    //初始化为单位矩阵
    
        for(; k; k >>= 1) {
            if(k&1) c = c*a;
    
            a = a*a;
        }
        return c;
    }
    
    void Build_Matrix() {
        CL(g.mat, 0);
        int i, j;
        for(i = 0; i < node_num; ++i) {
            if(node[i].end) continue;
            for(j = 0; j < 4; ++j) {
                if(node[i].next[j] != -1 && node[node[i].next[j]].end == 0) g.mat[i][node[i].next[j]]++;    //goto line105
    
                else if(node[i].next[j] == -1) {
                    if(i == 0)  {g.mat[i][i]++; continue;}
                    int tmp = i;
                    while(node[tmp].next[j] == -1) {
                        tmp = node[tmp].fail;
                        if(tmp == 0)    break;
                    }
    
                    if(node[tmp].next[j] != -1 && node[node[tmp].next[j]].end == 0) g.mat[i][node[tmp].next[j]]++;
                    else if(node[tmp].next[j] == -1 && tmp == 0)    g.mat[i][0]++;
                }
            }
        }
    }
    
    LL solve(int n) {
        Build_Matrix();
        /*
        for(int i = 0; i < node_num; ++i) {
            for(int j = 0; j < node_num; ++j) {
                printf("%lld ", g.mat[i][j]);
            }
            puts("");
        }
        */
        g = g^n;
        LL ans = 0;
        for(int i = 0; i < node_num; ++i) {
            if(node[i].end != 0)    continue;
            ans = ans + g.mat[0][i];
            if(ans >= MOD)   ans %= MOD;
        }
        return ans;
    }
    
    int main() {
        //Read();
        //Write();
    
        int n, m, i;
    
        while(~scanf("%d%d", &m, &n)) {
            AC.Init();
            for(i = 0; i < m; ++i) {
                scanf("%s", ss);
                AC.Insert(ss);
            }
            AC.Build();
            printf("%lld\n", solve(n));
        }
        return 0;
    }

    AC自动机+DP

    DNA REPAIR

    给定n个疾病DNA序列,和一个待修复序列str。用最小的次数修改待修复序列,使其不含疾病DNA序列。

    dp[i][j]表示跑到母串第i位置,自动机上j状态时最小改变的字符数;

    dp[i+1][tmp] = min(dp[i+1][tmp], dp[i][j] + (st[i] ==k)); k表示从j状态到达tmp状态所走的边(注意区分dp的状态和自动机的状态)

    View Code
    //#pragma comment(linker,"/STACK:327680000,327680000")
    #include <iostream>
    #include <cstdio>
    #include <cmath>
    #include <vector>
    #include <cstring>
    #include <algorithm>
    #include <string>
    #include <set>
    #include <functional>
    #include <numeric>
    #include <sstream>
    #include <stack>
    #include <map>
    #include <queue>
    
    #define CL(arr, val)    memset(arr, val, sizeof(arr))
    #define REP(i, n)       for((i) = 0; (i) < (n); ++(i))
    #define FOR(i, l, h)    for((i) = (l); (i) <= (h); ++(i))
    #define FORD(i, h, l)   for((i) = (h); (i) >= (l); --(i))
    #define L(x)    (x) << 1
    #define R(x)    (x) << 1 | 1
    #define MID(l, r)   (l + r) >> 1
    #define Min(x, y)   (x) < (y) ? (x) : (y)
    #define Max(x, y)   (x) < (y) ? (y) : (x)
    #define E(x)        (1 << (x))
    #define iabs(x)     (x) < 0 ? -(x) : (x)
    #define OUT(x)  printf("%I64d\n", x)
    #define Read()  freopen("data.in", "r", stdin)
    #define Write() freopen("data.out", "w", stdout);
    
    typedef long long LL;
    const double eps = 1e-8;
    const double pi = acos(-1.0);
    const int inf = ~0u>>2;
    
    
    using namespace std;
    
    const int maxn = 1024;
    
    struct Node {
        int fail;
        int next[4];
        int cnt;
        bool end;
        void init() {
            fail = -1;
            cnt = 0;
            CL(next, -1);
            end = false;
        }
    };
    
    Node node[maxn];
    int node_num;
    int dp[maxn][maxn];
    int q[maxn];
    
    int get(char c) {
        switch(c) {
            case 'A' : return 0;
            case 'T' : return 1;
            case 'G' : return 2;
            case 'C' : return 3;
            default  : return -1;
        }
    }
    
    class AC_automaton {
    public:
        int head, tail;
        void init() {
            for(int i = 0; i < maxn; ++i)   node[i].init();
            head = tail = 0;
            node_num = 1;
        }
        void Insert(char* st) {
            int p = 0, t;
            for( ; *st; ++st) {
                t = get(*st);
                if(node[p].end) break;
                if(node[p].next[t] == -1) {
                    node[p].next[t] = node_num++;
                }
                p = node[p].next[t];
            }
            node[p].end = true;
        }
        void Build() {
            int tmp, p, i;
            q[tail++] = 0;
    
            while(head < tail) {
                tmp = q[head++];
                for(i = 0; i < 4; ++i) {
                    if(node[tmp].next[i] == -1) continue;
                    if(tmp == 0)    node[node[tmp].next[i]].fail = 0;
                    else {
                        p = node[tmp].fail;
                        while(p != -1) {
                            if(node[p].next[i] != -1) {
                                node[node[tmp].next[i]].fail = node[p].next[i];
                                if(node[node[p].next[i]].end)   node[node[tmp].next[i]].end = true;
                                break;
                            }
                            p = node[p].fail;
                        }
                        if(p == -1) node[node[tmp].next[i]].fail = 0;
                    }
                    q[tail++] = node[tmp].next[i];
                }
            }
        }
        int search(char* st) {
            int res = 0, t;
            int p = 0;
    
            for( ; *st; ++st) {
                t = get(*st);
                while(node[p].next[t] == -1 && p != 0)  p = node[p].fail;
                p = node[p].next[t];
                if(p == -1) p = 0;
                int tmp = p;
                while(tmp != 0 && node[tmp].cnt ) {
                    res += node[tmp].cnt;
                    node[tmp].cnt = -1;
                    tmp = node[tmp].fail;
                }
            }
            return res;
        }
    }AC;
    
    char st[maxn];
    
    int solve() {
        int i, j, k, tmp, len = strlen(st);
        for(i = 0; i <= len; ++i) {
            for(j = 0; j <= node_num; ++j)   dp[i][j] = inf;
        }
        dp[0][0] = 0;
    
        for(i = 0; i < len; ++i) {
            for(j = 0; j < node_num; ++j) {
                if(dp[i][j] == inf)  continue;
                for(k = 0; k < 4; ++k) {
                    tmp = j;
                    while(tmp != -1) {
                        if(node[tmp].next[k] != -1) {tmp = node[tmp].next[k]; break;}
                        tmp = node[tmp].fail;
                    }
                    if(tmp == -1)   tmp = 0;
                    if(node[tmp].end == false)  dp[i+1][tmp] = min(dp[i+1][tmp], dp[i][j] + (get(st[i]) != k));
                }
            }
        }
        int ans = inf;
        for(j = 0; j < node_num; ++j) {
            ans = min(ans, dp[len][j]);
        }
        return ans == inf ? -1 : ans;
    }
    
    
    int main() {
        //Read();
    
        int n, i, cas = 0;
        while(scanf("%d", &n), n) {
            AC.init();
            for(i = 0; i < n; ++i) {
                scanf("%s", st);
                AC.Insert(st);
            }
            scanf("%s", st);
            AC.Build();
            printf("Case %d: %d\n", ++cas, solve());
        }
        return 0;
    }

     

     

     

     

     

     

     

  • 相关阅读:
    Bolero and Data Mining
    2007年3月15日 网站论坛出现以下错误/forum/inc/Dv_ClsMain.asp,行 1344
    A Probabilistic Model for Retrospective News Event
    信息抽取的资料文档
    Textual Data Mining and WEBSOM
    DockPanel Suite更新到2.6了 武胜
    Use Custom Events from your WCF ServiceHost http://www.codeproject.com/Tips/150702/UseCustomEventsfromyourWCFServiceHost 武胜
    Unable to convert MySQL date/time value to System.DateTime 解决方案 转 武胜
    XML 转义字符 武胜
    Using Nini .NET Configuration Library 武胜
  • 原文地址:https://www.cnblogs.com/vongang/p/2756422.html
Copyright © 2011-2022 走看看