字符串

对串的操作:

从下标1开始数

insert(x)插入到第x个位置之前(新插入的元素位置是x)

 delete(x)是正好删除第x个

最小表示法模板:洛谷P1368(这题我之前写过?一点印象都没了,这就是不复习的下场)(算了困了明早再写,今天周日,摸了)

#include <bits/stdc++.h>
using namespace std;
int n,a[600005];
int f(){
    int i=1,j=2,k=0;
    while(i<=n&&j<=n){
        if(a[i+k]>a[j+k])i+=k+1,k=0;
        else if(a[i+k]<a[j+k])j+=k+1,k=0;
        else if(a[i+k]==a[j+k])k++;
        if(i==j)i++; 
    }
    return min(i,j);
}
int main(){
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        a[i+n]=a[i];
    }
    for(int i=f(),j=0;j<n;j++){
        cout<<a[i+j]<<' ';
    }
    return 0;
}

 

提示:三指针+延长一倍+注意:指针移动以后可能会撞在一起

很奇怪,最小表示法里面有个细节一直不能理解:

初始条件:i=1,j=2,k=0,若a[i+k]>a[j+k],为什么i+=k+1这一步是合理的,如何确定i->i+k+1之间的循环同构串一定比j指向的更大(搞不懂)

搞懂了!

解释:i之所以能够自增k+1,是因为i->i+k是与j->j+k完全相同的,所以i直接跳到i+k+1去,跳过的那些就交给j去遍历吧!

马拉车算法:

洛谷P3805

#include <bits/stdc++.h>
using namespace std;
char str[22000010];
int cnt,C=-1,R=-1,r[22000010],res;
void in_() {
    str[cnt++] = '#';
    char c = getchar();
    while (c >= 'a' && c <= 'z')str[cnt++] = c, str[cnt++] = '#', c = getchar();
}
int process() {
    for (int i = 0; i < cnt; i++) {
        r[i] = i > R ? 1 : min(r[C * 2 - i], (R - i));
        while (i + r[i] < cnt && i - r[i] >= 0 && str[i + r[i]] == str[i - r[i]]) r[i]++;
        if (i + r[i] > R) R = i + r[i], C = i;
        res = res > r[i] ? res : r[i];
    }
    return res - 1;
}
int main() {
    in_();
    cout<<process();
    return 0;
}

KMP:

#include <bits/stdc++.h>
using namespace std;
string a, b;
int len_a, len_b;
int ne[1000005];
void get_next() {
    string c = b + ‘0’;//防止越界
    for (int i = 2, j = 0; i <= len_b; i++) {//j是前跳的指针
        while (j && c[i] != c[j + 1]) j = ne[j];//i和j指向的匹配不上,j一直前跳,直到匹配成功或者j已经到了最前面,无处可跳为止
        if (c[i] == c[j + 1])j++;//匹配上了
        ne[i] = j;//记录下来
        //cout<<"第"<<i<<"个字符的最长前后缀长度为"<<j<<endl;
    }
}
void process() {
    for (int i = 1, j = 0; i <= len_a; i++) {
        while (j && a[i] != b[j + 1])j = ne[j];//匹配不成功就一直前跳,直到找到或者无处可跳为止,j的意义有两个:1.b与a已经匹配到的位置,2.最长前后缀的长度
        if (a[i] == b[j + 1])j++;//找到
        if (j == len_b) cout << i - j + 1 << endl;//完成了匹配
    }
    for (int i = 1; i <= len_b;i++) {
        cout << ne[i] << " ";
    }
}
int main() {
    cin >> a >> b; 
    len_a = a.size(), len_b = b.size(); 
    a = '0' + a; b = '0' + b;//把下标排列整齐
    get_next();
    process();
    return 0;
}

来啊,让我们写个修改版:

 

#include <bits/stdc++.h>
using namespace std;
string a, b;
int len_a, len_b;
int ne[1000005];

void get_next() {
    int i = 1, j = 0;
    while (i < len_b) {
        if (j == 0 || b[i] == b[j]) {
            i++, j++;
            if (b[i] != b[j])ne[i] = j;
            else ne[i] = ne[j];
        }
        else j = ne[j];//
    }
}

void process() {
    int i = 1, j = 1;//i在主串上跑,j在模式串上跑
    while (i <= len_a && j <= len_b) {
        if (j == 0 || a[i] == b[j])i++, j++;
        //状况1:j==0,j:“我都跑到头了呀大哥,你别不动了,你稍微动一动吧,于是i和j都自增,i变下一个,j变1,从第一个元素开始遍历”;
        //状况2:匹配成功了,自增,匹配下一个
        else j = ne[j];//匹配失败,j指向fail指针指向的位置
    }
    if (j > len_b)cout << i - len_b;//i此时指向匹配成功之后的下一个字符
    else cout << "不存在";
}

int main() {
    cin >> a >> b;
    len_a = a.size(), len_b = b.size();
    a = '0' + a; b = '0' + b;//把下标排列整齐
    get_next();
    process();
    return 0;
}

以上是课本上的代码复现

手动求ne数组加强版本的时候,先把无加强版ne数组求出来,然后再求加强版ne数组。

做法:观察一下s[i]和s[ne[i]]是否相同,相同的话neplus[i]就是s[ne[i]],不同的话就是ne[i]本身。

PTA的字符串计数从1开始,而非0

 洛谷P3375

AC自动机:

建立Trie树,插入字符串:

//创建字典树
int idx, cnt[N], ch[N][26];
void insert(string s) {
    int p = 0;//从第0个结点,也就是根节点开始
    for (int i = 0; s[i]; i++) {
        int j = s[i] - 'a';//把字符映射为数字
        if (!ch[p][j])ch[p][j] = ++idx;//如果第p个结点处没有字符j的分支,则创建,并给它一个编号id
        p = ch[p][j];//以这个编号为id的结点为新结点
    }
    cnt[p]++;//最后这个结点的插入次数加一
}

 

 查找字符串:

 

 

int query(string s) {
    int p = 0;
    for (int i = 0; s[i]; i++) {
        int j = s[i] - 'a';
        if (!ch[p][j])return 0;
        p = ch[p][j];
    }
    return cnt[p];
}

完整AC自动机:洛谷P3808

(59条消息) AC自动机 算法详解(图解)及模板_bestsort的博客-CSDN博客_ac自动机算法

 

 

 

 

 代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2 * 1e6 + 9;

int trie[maxn][26]; //字典树
int cntword[maxn];  //记录该单词出现次数
int fail[maxn];     //失败时的回溯指针
int cnt = 0;

void insert(string s) {
    int p = 0;
    for (int i = 0; s[i]; i++) {
        int j = s[i] - 'a';
        if (!trie[p][j])trie[p][j] = ++cnt;
        p = trie[p][j];
    }
    cntword[p]++;
}
void getFail() {
    queue <int>q;
    for (int i = 0; i < 26; i++) {      //将第二层所有出现了的字母扔进队列
        if (trie[0][i]) {
            fail[trie[0][i]] = 0;
            q.push(trie[0][i]);
        }
    }

    //fail[now]    ->当前节点now的失败指针指向的地方
    //tire[now][i] ->下一个字母为i + 'a'的节点的下标为tire[now][i]
    while (!q.empty()) {
        int now = q.front();
        q.pop();

        for (int i = 0; i < 26; i++) {      //查询26个字母
            if (trie[now][i]) {
            //如果有这个子节点为字母i+'a',则让这个节点的fail指针指向他父亲节点的fail指针所指向的那个节点的子节点
            //有子结点,为子结点建立回跳边,使用四边形规则    
                fail[trie[now][i]] = trie[fail[now]][i];
                q.push(trie[now][i]);//子结点入队
            }
            else
            //否则就让当前节点的这个子节点指向当前节点fail指针的这个子节点
            //没子结点,为自己建立转移边,使用三角形规则
                trie[now][i] = trie[fail[now]][i];
        }
    }
}

int query(string s) {
    int now = 0, ans = 0;
    for (int i = 0; i < s.size(); i++) {    //遍历文本串
        now = trie[now][s[i] - 'a'];  //从s[i]点开始寻找
        for (int j = now; j && cntword[j] != -1; j = fail[j]) {
            //一直向下寻找,直到匹配失败(失败指针指向根或者当前节点已找过).
            ans += cntword[j];
            cntword[j] = -1;    //将遍历后的节点标记,防止重复计算
        }
    }
    return ans;
}

int main() {
    int n;
    string s;
    cin >> n;
    for (int i = 0; i < n; i++) {
        cin >> s;
        insert(s);
    }
    fail[0] = 0;
    getFail();
    cin >> s;
    cout << query(s) << endl;
    return 0;
}

 now是一整层一整层地下跳,j是层层往上找有没有后缀与now所指结点的后缀相同的模式串,这一层j没有就再上一层,直到上不去了或者已经走过了为止。

基础操作:

#include<bits/stdc++.h>
using namespace std;
int main(){
    string s1;
    while(getline(cin,s1)){
        cout<<s1<<endl;
        //遍历
        cout<<"all elements of string:"<<endl;
        for(auto c:s1){
            cout<<c<<' ';
        }
        cout<<endl;
        //
        string s2=s1.substr(0,3);//从pos开始的len个字符
        cout<<"substring:"<<endl;
        cout<<s2<<endl;
        //
        string s3(s1);
        s3=s3.insert(3,s3);
        cout<<"string inserted:"<<s3<<endl;
        //
        s3.erase(3,9);//从3到9
        cout<<"after erasing:"<<s3<<endl;
        //
        auto temp=s3.find(string("456"),2);//从第pos个下标开始找
        cout<<"first appearing position in the string:";
        if(temp!=string::npos){
            cout<< temp<<endl;
        } else{
            cout<<"not found"<<endl;
        }
        //替换
        s3.replace(s3.find("456"),3,"66666666");
        cout<<"after replace:"<<s3<<endl;
    }

    return 0;
}

 

posted @ 2022-08-05 22:19  _a_rk  阅读(50)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end