加载中...

串匹配

对若干个字符串前后缀匹配的问题一般进行预处理
对已知单词数量的字符串固定单词的字符串 s[n] 每次cin就可以了

 for (int i = 0; i < n; i ++ ){
        for (int j = 0; j < n; j ++ ){//若干个字符串 匹配的预处理操作 

使用答案和新产生的string中取得的最大值 需要int转化 否则不通过
ans=max(ans,(int)string.size() )

kmp

#include <iostream>

using namespace std;

const int N = 100010, M = 1000010;

int n, m;
int ne[N];
char s[M], p[N];

int main()
{
    cin >> n >> p + 1 >> m >> s + 1;//这里做了处理 在+1处改变
//ne[j ]  记录 最大的长度 长度是指一个字符串 长度为j 的前缀(从1-j) 和 后缀(从末尾前长度个字母到末尾)匹配的  最长不可以超过本身
//于是不满足的话 直接扔前缀过去就可以了 让前缀和后缀成功对齐 j从ne【j】
    //两个下标从1开始
    for (int i = 2, j = 0; i <= n; i ++ )//对模式串操作 先求next数组 从2开始才有意义 i一直再动 只有j往回走
    {
        while (j && p[i] != p[j + 1]) j = ne[j];//j不为0否则j=ne【j】会死循环 i和j需要一样 while是必要的不相等 j匹配下一个可能值可能是ne[j] 如果不是就是ne[ne[j]]
         //先看下面
        //下面的j+1是唯一将j从下标0解放出来的东西 所以j+1可以表示当前比较的下表 而j表示长度
        if (p[i] == p[j + 1]) j ++ ;//j本来就是值=指的是前面的长度 这时候模式串相等就说明还可以加一个长度
        ne[i] = j;//之所以不把这步和上面一起变成ne[i]=++j 是因为if   
    }

    for (int i = 1, j = 0; i <= m; i ++ )//这里i从0 因为不是求ne数组 他的值是有意义的 j后退了 但是作为比较的i是不动的 
    {
        while (j && s[i] != p[j + 1]) j = ne[j];//不匹配 让子串指针j 不一致回到ne[j]的位置
        
        if (s[i] == p[j + 1]) j ++ ;//剩下 如果匹配成功 让j往后走
        
        if (j == n)//n是子串的长度j成功走到这里子串的最后 字串成功匹配到最后 说明找到了一个匹配的这个位置 这里比上面多了一步处 
        {
            printf("%d ", i - n);//输出位置,这里n很关键 但是i不动 找下一个
            j = ne[j];//j返回,重新匹配
        }
    }

    return 0;
}


链接:https://ac.nowcoder.com/acm/contest/23479/A

来源:牛客网

小红很喜欢红色(用'R'字母表示),但她非常讨厌紫色(用'P'字母表示)。
她想取一个连续子串,该子串包含至少 k 个'R'字符,且不能包含'P'字符。
你能告诉她有多少合法的方案可以取到吗?
注:只要连续子串的起始位置或终止位置不同,我们就认为是两个不同的方案。

识别到p就切割 问题转换成 统计满足k个p的字串有多少个
记录一个temp的数组
因为是子串 所以右端到左端满足k个的时候,就将
左端点向右走几次 就说明这个子串(右端点固定,左端点长度,长度减少的字串的数量)有几个
如 **rrr 区间不断减少
rrr
r
rr
满足条件的字串有两个 为j走过的(减少的)数量
res+=j;

#include<bits/stdc++.h>
using namespace std;
int n,k;
string s;
long long res=0;
void gao(string t){    //求字符串t中有多少子串包含至少k个'R'
    int i,j=0,temp=0;
    for(i=0;i<t.length();i++){//枚举右端点,找第一个不合法的左端点
        temp+=t[i]=='R';
        while(j<=i&&temp==k){
            temp-=t[j++]=='R';
        }
        //在这个左端点前面的都是合法的。
        res+=j;

    }
}
int main(){
    int i;
    cin>>n>>k>>s;
    string t="";
    for(i=0;i<n;i++){
        if(s[i]=='P')gao(t),t="";
        else t+=s[i];
    }
    gao(t);
    cout<<res;
}

一个从0开始的不需要j=-1和初始化的版本~


#include <bits/stdc++.h>

using namespace std;

const int N = 100010;
int ne[N];
int n, m;
string p, s;

int main(){
    cin >> n >> p >> m >> s;

    //i是代匹配位置,j是以匹配长度,j - 1是以匹配的最后一个位置
    //与自身匹配i从1开始
    for(int i = 1, j = 0; i < n; i ++){
        while(j && p[i] != p[j]) j = ne[j - 1];
        if(p[i] == p[j]) j ++;
        ne[i] = j;
    }

    //与长字符串匹配i从0开始
    for(int i = 0, j = 0; i < m; i ++){
        while(j && s[i] != p[j]) j = ne[j - 1];
        if(s[i] == p[j]){
            j ++;
            if(j == n){
                cout << i - n + 1 << " ";
                j = ne[n - 1];
            }
        }
    }
    return 0;
}

字典树 https://www.acwing.com/problem/content/837/ 关键在于idx

#include <iostream>

using namespace std;

const int N = 100010;

int son[N][26], cnt[N], idx;//son[N]是idx数 cnt统计这个idx个数 
char str[N];

void insert(char *str)
{
    int p = 0;
    for (int i = 0; str[i]; i ++ )
    {
        int u = str[i] - 'a';//
        if (!son[p][u]) son[p][u] = ++ idx;//没有就新建 son存储的是idx的儿子 也是idx
        p = son[p][u];//p是儿子 变成idx
    }
    cnt[p] ++ ;
}

int query(char *str)
{
    int p = 0;
    for (int i = 0; str[i]; i ++ )
    {
        int u = str[i] - 'a';
        if (!son[p][u]) return 0;
        p = son[p][u];
    }
    return cnt[p];//这里的p不是某个字母 而是存储到的idx
}

int main()
{
    int n;
    scanf("%d", &n);
    while (n -- )
    {
        char op[2];
        scanf("%s%s", op, str);
        if (*op == 'I') insert(str);
        else printf("%d\n", query(str));
    }

    return 0;
}

最大异或对https://www.acwing.com/video/63/

对于每个数 找到一个能提供最多 每位是相反数的数 另答案有最多的1就是最大的
于是把数字转化成二进制 先存最高位

#include <iostream>
#include <algorithm>

using namespace std;

const int N = 100010, M = 3100010;

int n;
int a[N], son[M][2], idx;

void insert(int x)
{
    int p = 0;
    for (int i = 30; i >= 0; i -- )
    {
        int &s = son[p][x >> i & 1];//传引用son 为了下面方便操作s 也可以写成int s=x>>i &i; if(!son[p][s]) son[p][s]=++idx;p=son[p][s];
        if (!s) s = ++ idx;
        p = s;
    }
}

int search(int x)
{
    int p = 0, res = 0;
    for (int i = 30; i >= 0; i -- )
    {
        int s = x >> i & 1;
        if (son[p][!s])//找到和他相反的
        {
            res += 1 << i;//贡献一个i位的1
            p = son[p][!s];
        }
        else p = son[p][s];
    }
    return res;
}

int main()
{
    scanf("%d", &n);
    for (int i = 0; i < n; i ++ )
    {
        scanf("%d", &a[i]);
        insert(a[i]);
    }

    int res = 0;
    for (int i = 0; i < n; i ++ ) res = max(res, search(a[i]));

    printf("%d\n", res);

    return 0;
}

蓝桥杯:交换瓶子https://www.acwing.com/problem/content/1226/

实际上是 找环 :
利用st标记有没有在环里面
每次j=a[j] 将下一个点加到点里面

int main()
{
    cin>>n;
    int cnt=0;
    for(int i=1;i<=n;i++) cin>>a[i];
    for(int i=1;i<=n;i++){
        
        if(!st[i]){
        for (int j = i; !st[j]; j=a[j] ){
        st[j]=true;
        }
        cnt++;
        }
    }
    // cout << cnt;
    cout<<n-cnt<<endl;
    return 0;
} 

出现次数 https://www.acwing.com/problem/content/4315/

每次询问给一个区间[l,r],问我们T在S的子串(str)S[l,r](前缀和)中出现了几次

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 1010;

int n, m, Q;
string S, T;

int s[N];

int main()
{
    scanf("%d%d%d", &n, &m, &Q);
    scanf("%s%s", strs, strt);
    S = strs, T = strt;
    S = ' ' + S;

    for (int i = m; i <= n; i ++ )//选择终点表示串
    {
        s[i] = s[i -1 ];
        if (S.substr(i - (m-1), m) == T)//看看这个长度的字串是不是满足 substr( 起始点,长度) i本身算一个字符串
            s[i] ++ ;
    }

    while (Q -- )
    {
        int l, r;
        scanf("%d%d", &l, &r);
        l += (m - 1);//l这里的是l是前缀和的l代表串的个数 l这里本身算一个字符 所以剩下的是m-1个字符 才可以代表
        if (l > r) puts("0");
        else printf("%d\n", s[r] - s[l - 1]);//
    }

    return 0;
}

字串变化

一个字符有n个字符进行操作 查询得到相应的字符

#include <iostream>
#include <cstring>
#include <algorithm>
#include <unordered_map>
#include <queue>

using namespace std;
const int N = 6;
int n;
string A,B;
string  a[N],b[N];
queue<string >qa,qb;
unordered_map<string ,int>da,db;
int extend(queue<string>& q,unordered_map<string ,int >&da ,unordered_map<string ,int>&db,string  a[],string b[]){
    int d=da[q.front()];
    while(q.size()&&da[q.front()]==d){
        auto t=q.front() ;
        q.pop();
        for (int i = 0; i < n; i ++ ){
            for (int j = 0; j <t.size() ; j ++ ){
                
                if(t.substr(j,a[i].size())==a[i]){
                    string r=t.substr(0,j)+b[i]+t.substr(j+a[i].size());
                    if(db.count(r)) return da[t]+db[r]+1;
                    if(da.count(r)) continue;
                    q.push(r);
                    da[r]=da[t]+1;
                    
                    
                }
            }
        }
        
    }
    return 11;
}
int bfs(){
    if(A==B) return 0;
    qa.push(A);qb.push( B);
    da[A]=0,db[B]=0;
    int step=0;
    while(qa.size()&&qb.size()){
        int t;
        if(qa.size()<qb.size()) t=extend(qa , da,db , a,b );
        else t=extend( qb,db ,da ,b ,a);
        if(t<=10) return t;
        if(++step==10) return -1;
    }
    return -1;
    
    
}


int main()
{
    cin >> A>>B;
    while(cin>>a[n]>>b[n]) n++;
    
    int t=bfs();
    if(t==-1) cout << "NO ANSWER!"<<endl;
    else cout << t<<endl;
    return 0;
}

单词接龙

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 21;

int n,m;
string word[N];
int ans;
int used[N];
int g[N][N];

int dfs(string dragon,int last){
    ans=max((int)dragon.size(),ans);
    used[last]++;
    
    for(int i=0;i<n;i++){
        if(g[last][i]&&used[i]<2){
            dfs(dragon+word[i].substr(g[last][i]),i);
            
        }
    }
    
    used[last]--;
}

int main()
{
    cin >> n;
    char start;
    for (int i = 0; i < n; i ++ ){
        cin >> word[i];
        
        
    }
    cin >> start;
    for (int i = 0; i < n; i ++ ){
        for (int j = 0; j < n; j ++ ){//若干个字符串 匹配的预处理操作
            
            for (int k = 1; k <min(//最小长度 word[i].size(),word[j].size()); k ++ )
            if(word[i].substr( word[i].size()-k,k ) == word[j].substr( 0,k)) {
                g[i][j]=k;
                break;
            }
            
        }
    }
    for (int i = 0; i < n; i ++ ){
        if(word[i][0]==start)
        dfs(word[i],i);
    }
    cout << ans;
    return 0;
}

牛年 https://www.acwing.com/problem/content/3373/

给出一个原点
给出固定单词数的句话 只有某三个单词可以有用
通过这个单词获得各个点的差值

#include <iostream>
#include <cstring>
#include <algorithm>
#include <unordered_map>

using namespace std;

unordered_map<string, int> id = {
    {"Ox", 0}, {"Tiger", 1}, {"Rabbit", 2},
    {"Dragon", 3}, {"Snake", 4}, {"Horse", 5}, 
    {"Goat", 6}, {"Monkey", 7}, {"Rooster", 8}, 
    {"Dog", 9}, {"Pig", 10}, {"Rat", 11}
};


int main()
{
    unordered_map<string, int> p;
    p["Bessie"] = 0;//p[string]表示string 和某个单词差距是多少

    int n;
    cin >> n;
    while (n -- )
    {
        string s[8];//获得每一句话的各个单词
        for (int i = 0; i < 8; i ++ ) cin >> s[i];//

        if (s[3] == "previous")//画个数轴说明上一个年份在当前的年份的左边
        {
            int x = p[s[7]], y = id[s[4]];//x是上一个牛相差多少年 y是多少年
            int r = ((x - y) % 12 + 12) % 12;//r是这个牛 和 过了y年之后 距离s相差多少年
            if (!r) r = 12;//r是0说明相差12年 就要换回去12
            p[s[0]] = x - r;//画条数轴 s[0]年份在x左边
        }
        else
        {
            int x = p[s[7]], y = id[s[4]];
            int r = ((y - x) % 12 + 12) % 12;
            if (!r) r = 12;
            p[s[0]] = x + r;
        }
    }

    cout << abs(p["Elsie"]) << endl;
    return 0;
}


posted @ 2022-02-09 23:20  liang302  阅读(58)  评论(0编辑  收藏  举报