字符串——广义&二维KMP

字符串——广义&二维KMP

广义KMP

普通KMP是关于字符序列的一种模式匹配算法,而广义KMP将字符序列推广到一般序列,即带有等价关系的序列均可以进行广义KMP算法。

即我们只需要定义序列中的元素是否相等或不相等,即可像普通KMP一样执行广义KMP算法。

二维KMP

广义KMP的一种特殊实现就是二维KMP,在二维KMP中的序列元素就是字符串。例如序列{“abcd”,“addfd”,“dwffas”},我们可以定义字符串相等,那么就可以在字符串序列上执行广义KMP算法,即二维KMP算法。

POJ 2185

此题要求求一个二维字符序列中的二维最小循环子矩阵的面积。

我们可以将每一列当成一个字符串,则构成一个字符串序列。每一行当成一个字符串,构成一个字符串序列。对这两个字符串序列分别做KMP算法。然后分别求出最小循环节的长度,相乘便是答案,正确性显然。

vector<string> vec;

int kmp2(vector<string> &arr)
{
    vector<int> nxt;
    nxt.push_back(0);
    for (int i = 1; i < arr.size(); i++)
    {
        int d = nxt[i - 1];
        while (arr[d] != arr[i] && d != 0)
            d = nxt[d - 1];
        nxt.push_back(arr[d] == arr[i] ? d + 1 : 0);
    }

    return arr.size() - nxt.back();
}

void solve()
{
    int n, m;
    cin >> n >> m;
    for (int i = 0; i < n; i++)
    {
        string str;
        cin >> str;
        vec.push_back(str);
    }
    vector<string> col, row;
    for (int c = 0; c < m; c++)
    {
        string str;
        for (int r = 0; r < n; r++)
            str.push_back(vec[r][c]);
        col.push_back(str);
    }
    for (int r = 0; r < n; r++)
    {
        string str;
        for (int c = 0; c < m; c++)
            str.push_back(vec[r][c]);
        row.push_back(str);
    }
    cout << kmp2(col) * kmp2(row) << endl;
}

2021 CCPC for Femal F

这题是上一题的加强版,我们只需要使用一层字符串hash来优化一维判断即可。


const int MOD353 = 998244353;
const int BASE = 114513;

char str[2005][2005];
ll bit[10005];
ll colh[2005][2005], rowh[2005][2005];
int nxt[2005];

struct Sequence
{
    int p;
    int l;
    int r;
};

ll kmp2(int p1, int p2, int l, int r, ll vhash[][2005])
{
    auto cmp = [&](int a, int b)
    {
        int len = r - l + 1;
        ll ah = (vhash[a][r] - (vhash[a][l - 1] * bit[len]) % MOD353 + MOD353) % MOD353;
        ll bh = (vhash[b][r] - (vhash[b][l - 1] * bit[len]) % MOD353 + MOD353) % MOD353;
        return ah == bh;
    };

    nxt[0] = 0;
    int n = (p2 - p1 + 1);
    for (int i = 1; i < n; i++)
    {
        int d = nxt[i - 1];
        while (!cmp(d + p1, i + p1) && d != 0)
            d = nxt[d - 1];
        nxt[i] = cmp(d + p1, i + p1) ? d + 1 : 0;
    }

    return n - nxt[n - 1];
}

void solve()
{
    bit[0] = 1;
    for (int i = 1; i < 10005; i++)
    {
        bit[i] = (bit[i - 1] * BASE) % MOD353;
    }
    int n, q;
    scanf("%d %d", &n, &q);
    for (int i = 1; i <= n; i++)
        scanf("%s", str[i] + 1);

    for (int c = 1; c <= n; c++)
    {
        ll *h = colh[c];
        for (int r = 1; r <= n; r++)
            h[r] = ((h[r - 1] * BASE) % MOD353 + str[r][c]) % MOD353;
    }
    for (int r = 1; r <= n; r++)
    {
        ll *h = rowh[r];
        for (int c = 1; c <= n; c++)
            h[c] = ((h[c - 1] * BASE) % MOD353 + str[r][c]) % MOD353;
    }

    for (int i = 0; i < q; i++)
    {
        int r1, c1, r2, c2;
        cin >> r1 >> c1 >> r2 >> c2;
        cout << kmp2(r1, r2, c1, c2, rowh) * kmp2(c1, c2, r1, r2, colh) << endl;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值