Java教程

字符串

本文主要是介绍字符串,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

哈希与哈希表

• 使用一个哈希函数将某个特定的数字变成另一个数字,这种操作称之为hash。 • 通常我们会以取模运算来作为哈希函数。 • 举例: hash(key)=key%23, 这样数组 [1 ,75,324] -> [1 ,6,2] • 如果哈希后得到的值相同,我们则可用该值建一个链表,把相同的值都放一起, 这样我们就可以得到一个哈希表。 核心代码

 

字符串hash 

 

• 考虑这样一个问题:

 

• 有m个字符串,总长S。 q次询问两个字符串是否完全一样。 • 数据范围1 0^5。 优化做法: • 一个相对普适的做法是这样的: • 将这个字符串(假设只有小写字母)视为一个27进制数,将a看作1, b看作2,依此类推。 • 比如‘abca’看作1 *27^3+2*27^2+3*27+1。 • 但这样在字符串较大时数字会很大,存不下来。 • 一个欺骗方法是找一个较大的数P(最好是大质数),只记录转换后数字对P 取模的结果。 核心代码

 

 

 哈希冲突 

 

 

 

自然溢出 

 

 

 

 子串hash 

 

 

 哈希与回文串

 

例题:

 

 

大体思路: 设 L[i] 表示 S 中最小的下标,它满足从它开始的长度为 i 个字符是 T[1…i]. 同时还需要满足 L[i]+i >= k.(否则长度不够, 没法取这个字符串)。 没有满足条件的下标则记为 -1 . 同理,设 R[i] 表示 S 中最大的下标,满足以它结尾的前 i 个字符是 T[mi+1…m]. 同时还满足 R[i]-I <= n-k+1 .(否则右串的长度不够,没法取)。 没有满足条件的下标则记为 -1 . 求 L 和 R 数组需要用到扩展KMP算法。 扩展KMP目标是求2个串的最长公共前缀。 假设我们求得 extend[i] 表示 S[i...n] 和 T 的最长公共前缀。 要求 L[i] 即 找到 extend[x]>=i 且 x+i>=k. 时间复杂度O(n^2). 一种优化的方案是:先对extend数组排序,然后从大到小枚举 i,每次把 extend[x]>=i 的 x 加入一个集合。 询问的就是 x>=k-i 的最小的 x。可以用一个 set 在O(logn) 时间内完成。 (s.lower_bound(k-i) )。 翻转 S 和 T,同理求出 R 数组。
// problem: CF955D
#include <bits/stdc++.h>
using namespace std;

#define pb push_back
#define mk make_pair
#define lob lower_bound
#define upb upper_bound
#define fi first
#define se second
#define SZ(x) ((int)(x).size())

typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;

template<typename T> inline void ckmax(T& x, T y) { x = (y > x ? y : x); }
template<typename T> inline void ckmin(T& x, T y) { x = (y < x ? y : x); }

const int MAXN = 5e5;
const int LOG = 18;
const int INF = 1e9;

class KMP {
public:
    int fail[MAXN + 5], match[MAXN + 5];
    void get_fail(const char* t, int m) {
        fail[1] = 0;
        for (int i = 2, j = 0; i <= m; ++i) {
            while (j && t[i] != t[j + 1])
                j = fail[j];
            if (t[i] == t[j + 1])
                ++j;
            
            fail[i] = j;
            // cerr << j << " \n"[i == m];
        }
    }
    void get_match(const char* s, const char* t, int n, int lim) {
        for (int i = 1, j = 0; i <= n; ++i) {
            while (j && (s[i] != t[j + 1] || j >= lim))
                j = fail[j];
            if (s[i] == t[j + 1])
                ++j;
            
            match[i] = j;
            // cerr << j << " \n"[i == n];
        }
    }
    KMP() {}
};

class Tree {
public:
    struct EDGE { int nxt, to; } edge[MAXN * 2 + 5];
    int head[MAXN + 5], tot;
    inline void add_edge(int u, int v) { edge[++tot].nxt = head[u]; edge[tot].to = v; head[u] = tot; }
    
    int fa[MAXN + 5], sz[MAXN + 5], son[MAXN + 5], dep[MAXN + 5];
    void dfs1(int u) {
        sz[u] = 1;
        for (int i = head[u]; i; i = edge[i].nxt) {
            int v = edge[i].to;
            if (v == fa[u])
                continue;
            fa[v] = u;
            dep[v] = dep[u] + 1;
            dfs1(v);
            sz[u] += sz[v];
            if (!son[u] || sz[v] > sz[son[u]])
                son[u] = v;
        }
    }
    int top[MAXN + 5], dfn[MAXN + 5], ofn[MAXN + 5], rev[MAXN + 5], cnt_dfn;
    void dfs2(int u, int t) {
        top[u] = t;
        dfn[u] = ++cnt_dfn;
        rev[cnt_dfn] = u;
        if (son[u])
            dfs2(son[u], t);
        for (int i = head[u]; i; i = edge[i].nxt) {
            int v = edge[i].to;
            if (v == fa[u] || v == son[u])
                continue;
            dfs2(v, v);
        }
        ofn[u] = cnt_dfn;
    }
    bool is_anc(int anc, int v) {
        return dfn[anc] <= dfn[v] && ofn[anc] >= dfn[v];
    }
    Tree() {}
};

class StaticRangeCover {
private:
    int len;
    int fa[MAXN + 5], sz[MAXN + 5], mx[MAXN + 5], res[MAXN + 5];
    vector<pair<pii, int> > vec;
    int get_fa(int u) {
        return (u == fa[u]) ? u : (fa[u] = get_fa(fa[u]));
    }
    void unite(int u, int v) {
        u = get_fa(u);
        v = get_fa(v);
        if (u != v) {
            if (sz[u] > sz[v])
                swap(u, v);
            fa[u] = v;
            sz[v] += sz[u];
            mx[v] = max(mx[v], mx[u]);
        }
    }
    void jump_and_cover(int l, int r, int val) {
        for (int i = l; i <= r; ++i) {
            i = mx[get_fa(i)];
            if (i > r) {
                break;
            }
            res[i] = val;
            unite(i, r + 1);
        }
    }
public:
    void build(int _len) {
        len = _len;
        for (int i = 1; i <= len + 1; ++i) {
            fa[i] = i;
            sz[i] = 1;
            mx[i] = i;
            res[i] = INF;
        }
    }
    void range_cover(int l, int r, int v) {
        vec.pb(mk(mk(l, r), v));
    }
    void get_res(int* _res) {
        for (int i = SZ(vec) - 1; i >= 0; --i) {
            jump_and_cover(vec[i].fi.fi, vec[i].fi.se, vec[i].se);
        }
        for (int i = 1; i <= len; ++i) {
            _res[i] = res[i];
        }
    }
    StaticRangeCover() {}
};

class RangeMinQuery {
private:
    int _log2[MAXN + 5];
    pii st[MAXN + 5][LOG + 1];
public:
    void build(int* a, int n) {
        _log2[0] = -1;
        for (int i = 1; i <= n; ++i) {
            _log2[i] = _log2[i >> 1] + 1;
            st[i][0] = mk(a[i], i);
        }
        for (int j = 1; j <= LOG; ++j) {
            for (int i = 1; i + (1 << (j - 1)) <= n; ++i) {
                st[i][j] = min(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]);
            }
        }
    }
    pii rmq(int l, int r) {
        int k = _log2[r - l + 1];
        return min(st[l][k], st[r - (1 << k) + 1][k]);
    }
    int rmq_val(int l, int r) { return rmq(l, r).fi; }
    int rmq_pos(int l, int r) { return rmq(l, r).se; }
    RangeMinQuery() {}
};

int n, m, K;
char s[MAXN + 5], t[MAXN + 5];

KMP kmp, kmp_rev;
Tree tree, tree_rev;

StaticRangeCover DSU;
RangeMinQuery RMQ;

void cover_anc(int u, int tim) {
    while (u) {
        int l = tree.dfn[tree.top[u]], r = tree.dfn[u];
        DSU.range_cover(l, r, tim);
        u = tree.fa[tree.top[u]];
    }
}

int first_cov_tim[MAXN + 5];
int arr[MAXN + 5];
void query_anc(int u, int tim) {
    // u 的祖先, 有没有人在第 tim 时刻之前(严格小于)被覆盖了?
    while (u) {
        int l = tree_rev.dfn[tree_rev.top[u]], r = tree_rev.dfn[u];
        if (RMQ.rmq_val(l, r) < tim) {
            cout << "Yes" << endl;
            int x = tree_rev.rev[RMQ.rmq_pos(l, r)] - 1;
            for (int i = K; i < tim; ++i) {
                int v = kmp.match[i];
                if (v != 0 && tree.is_anc(x, v)) {
                    cout << i - K + 1 << " " << tim << endl;
                    exit(0);
                }
            }
            assert(0);
        }
        u = tree_rev.fa[tree_rev.top[u]];
    }
}
int main() {
    cin >> n >> m >> K;
    cin >> (s + 1);
    cin >> (t + 1);
    
    kmp.get_fail(t, m);
    kmp.get_match(s, t, n, min(m, K));
    reverse(s + 1, s + n + 1);
    reverse(t + 1, t + m + 1);
    kmp_rev.get_fail(t, m);
    kmp_rev.get_match(s, t, n, min(m, K));
    
    if (m <= K) {
        for (int i = m; i <= n - K; ++i) {
            if (kmp.match[i] == m) {
                cout << "Yes" << endl;
                cout << max(1, i - K + 1) << " " << n - K + 1 << endl;
                return 0;
            }
        }
        for (int i = K + 1; i <= n - m + 1; ++i) {
            if (kmp_rev.match[n - i + 1] == m) {
                cout << "Yes" << endl;
                cout << 1 << " " << min(i, n - K + 1) << endl;
                return 0;
            }
        }
    }
    
    for (int i = 2; i <= m; ++i) {
        if (kmp.fail[i]) {
            tree.add_edge(kmp.fail[i], i);
        }
        if (kmp_rev.fail[i]) {
            tree_rev.add_edge(m - kmp_rev.fail[i] + 1, m - i + 1);
            // cerr << "addedge(rev) " << m - kmp_rev.fail[i] + 1 << " " << m - i + 1 << endl;
        }
    }
    for (int i = 1; i <= m; ++i) {
        if (!tree.fa[i]) {
            tree.dfs1(i);
            tree.dfs2(i, i);
        }
    }
    for (int i = m; i >= 1; --i) {
        if (!tree_rev.fa[i]) {
            tree_rev.dfs1(i);
            tree_rev.dfs2(i, i);
        }
    }
    
    DSU.build(m);
    for (int i = n - K; i >= K; --i) {
        if (kmp.match[i] != 0) {
            int u = kmp.match[i];
            // cerr << "cover " << u << " " << i << endl;
            cover_anc(u, i);
        }
    } // 倒序添加, 预处理出每个位置第一次被覆盖是在什么时间
    
    DSU.get_res(first_cov_tim);
    for (int i = 1; i <= m; ++i) {
        if (tree_rev.rev[i] != 1) {
            arr[i] = first_cov_tim[tree.dfn[tree_rev.rev[i] - 1]];
            // cerr << arr[i] << endl;
        }
    }
    RMQ.build(arr, m);
    
    for (int i = K + 1; i <= n - K + 1; ++i) {
        if (kmp_rev.match[n - i + 1] != 0) {
            int u = m - kmp_rev.match[n - i + 1] + 1;
            // cerr << "query " << u << " " << i << endl;
            assert(u != 1);
            query_anc(u, i);
        }
    }
    cout << "No" << endl;
    return 0;
}

 


 

 

 

这篇关于字符串的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!