• 字符串专题


    月月查华华的手机
    题目描述:
    链接:https://ac.nowcoder.com/acm/problem/23053
    月月和华华一起去吃饭了。期间华华有事出去了一会儿,没有带手机。月月出于人类最单纯的好奇心,打开了华华的手机。哇,她看到了一片的QQ推荐好友,似乎华华还没有浏览过。月月顿时醋意大发,出于对好朋友的关心,为了避免华华浪费太多时间和其他网友聊天,她要删掉一些推荐好友。但是为了不让华华发现,产生猜疑,破坏了他们的友情,月月决定只删华华有可能搭讪的推荐好友。
    月月熟知华华搭讪的规则。华华想与某个小姐姐搭讪,当且仅当小姐姐的昵称是他的昵称的子序列。为了方便,华华和小姐姐的昵称只由小写字母构成。为了更加方便,保证小姐姐的昵称长度不会比华华的长。
    现在月月要快速的判断出哪些推荐好友要删掉,因为华华快回来了,时间紧迫,月月有点手忙脚乱,所以你赶紧写个程序帮帮她吧!

    分析:
    子序列的匹配问题 用到next[i][j] 表示距离第i个位置最近的字母j所在位置 具体实现可以逆着推 不断更新last[j] (表示最晚出现j的位置)

    最后就是匹配了

    code:

    #include<bits/stdc++.h>
    using namespace std;
    #define lowbit(x) x&(-x)
    #define ll long long
    const int maxn=1e6+5;
    char s[maxn],t[maxn];
    int n;
    int next[maxn][30];
    int last[30];
    void calc(); 
    int main(){
    	cin>>(s+1);
    	calc();
    	cin>>n;
    	for(int i=1;i<=n;i++){
    		cin>>(t+1);
    		int len=strlen(t+1);
    	    int now=0,pd=1;
    	    for(int j=1;j<=len;j++){
    		now=next[now][t[j]-'a'];
    		if(!now){
    	    pd=0;break;
    		}
    		}
    		if(pd)cout<<"Yes"<<endl;
    		else cout<<"No"<<endl;
    	}
         return 0;
    }
    void calc(){
    	int strl=strlen(s+1);
    	for(int i=strl;i>=0;i--){
    		for(int j=0;j<=25;j++)
    		next[i][j]=last[j];
    		last[s[i]-'a']=i;
    	}
    }
    

    最短编辑距离:

    链接:https://www.acwing.com/problem/content/904/

    给定两个字符串,a 和 b,现要将 a 变成 b。
    可以进行的操作有:
    1.将 a 中某个字符删除
    2.在 a 的某个位置插入某个字符
    3.将 a 中的某个字符替换成另一个字符
    问,最少几次操作可以实现 a 变成 b

    分析:
    设dp[i][j] 表示a字符串的前i个变成b字符串的前j个 最少操作步数

    删除: dp[i-1][j] + 1
    插入: dp[i][j-1] + 1
    替换: dp[i-1][j-1] +1 条件是a[i]!=b[i]

    code:

    #include <iostream>
    #include <algorithm>
    using namespace std;
    const int N = 1010;
    int n, m;
    char a[N], b[N];
    int f[N][N];//a的前N个去匹配b的前N个需要编辑的最小操作 
    int main() {
        scanf("%d%s", &n, a + 1);
        scanf("%d%s", &m, b + 1);
        for (int i = 0; i <= m; i ++ ) f[0][i] = i;//a的前0个字母去匹配b的前i个字母,只能加 
        for (int i = 0; i <= n; i ++ ) f[i][0] = i;//a的前i个字母去匹配b的前0个字母,只能减 
        for (int i = 1; i <= n; i ++ )
            for (int j = 1; j <= m; j ++ ) {//                      删除操作              增
                f[i][j] = min(f[i - 1][j] + 1, f[i][j - 1] + 1);//                        改
                if (a[i] == b[j]) f[i][j] = min(f[i][j], f[i - 1][j - 1]);
                else f[i][j] = min(f[i][j], f[i - 1][j - 1] + 1);
            }
        printf("%d\n", f[n][m]);
        return 0;
    }
    


    如果不知道这是一道背包题的话,很难往背包的方面想。我们不妨把题目中给定的6个单词看做六个数量无限的物品,现在他们要装到一个背包中

    比如要装一个input,能装入背包的条件是当前装了一些的背包中,再往后需要的字母依次是i,n,p,u,t。最后成功的条件是背包被装满即dp[串长]有值。

    dp[i]表示前i个字符是否能完成匹配。如上所述,则dp[i]能由dp[i - len[i]] 推出,当且仅当,子串c[j - len[i] ~ j] 为给定的单词

    这里可以用到substr,要尽量优化一下

    #include <cmath>
    #include <cstdio>
    #include <cstring>
    #include <cstdlib>
    #include <cctype>
    #include <map>
    #include <queue>
    #include <vector>
    #include <iostream>
    #include <algorithm>
    using namespace std;
     
    const int MAXM = 1000010;
    const int MAXN = 1010;
    int n;
    string c[] = {"", "one", "puton", "out", "output", "in", "input"};
    int len[] = {0, 3, 5, 3, 6, 2, 5};
    int dp[MAXM];
     
    int main( ) {
        scanf("%d", &n);
        string s; int l;
        for (int i = 1; i <= n; ++ i) {
            memset(dp, 0, sizeof(dp));
            cin >> s; 
            l = s.size( );
            dp[0] = 1;
            for (int j = 1; j <= l; ++ j)
            if(s[j - 1] == 'e' || s[j - 1] == 'n' || s[j - 1] == 't') //优化
                for (int i = 1; i <= 6; ++ i) 
                    if (j - len[i] >= 0) 
                    if(s[j - len[i]] == 'o' || s[j - len[i]] == 'p' || s[j - len[i]] == 'i') //优化
                        if (s.substr(j - len[i], len[i]) == c[i])
                            dp[j] = dp[j] | dp[j - len[i]];
            if (dp[l]) printf("YES\n");
            else printf("NO\n");
        }
    }
    

    https://www.luogu.com.cn/problem/P2890

    设dp[i][j]表示区间[i,j]变成回文串的最小花费,需要用到区间DP的思想。

    考虑如何用一个小区间更新一个大区间。

    如果大区间是dp[i][j],若s[i]=s[j],那么dp[i][j]=dp[i+1][j−1],即当前大区间可以由去掉其两端的小区间更新而来而不用花费。

    不等的时候,dp[i][j]=min(dp[i+1][j]+min(add[s[i]],del[s[i]]),dp[i][j−1]+min(add[s[j],del[s[j]]])

    关键在于前i个和前j个匹配 至于具体怎么匹配的我们不需要晓得

    #include<iostream>
    #include<algorithm>
    using namespace std;
     
    const int M = 2005, N = 256;
    int n, m;
    char c, s[M];
    int del[N], add[N], f[M][M];
     
    int main() {
        scanf("%d%d%s", &n, &m, (s+1));
        for (int i = 1; i <= n; ++ i) {
            cin >> c; 
            cin >> add[c] >> del[c];
        } 
        for (int L = 2; L <= m; ++ L) 
            for (int i = 1; i + L - 1 <= m; ++ i) {
                int j = i + L - 1;
                if (s[i] == s[j]) f[i][j] = f[i + 1][j - 1];
                else f[i][j] = min(f[i+1][j] + min(add[s[i]], del[s[i]]),
                                   f[i][j-1] + min(add[s[j]], del[s[j]]));
            }
        cout<<f[1][m]<<endl;
        return 0;
    }
    

    https://www.luogu.com.cn/problem/P1136

    注意这种交换位置类型的题目

    #include<bits/stdc++.h>
    using namespace std;
    inline int read(){
        int x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48),ch=getchar();}
        return x*f;
    }
    char s[501];
    int dp[501][101][101][3];
    int n,k,m;
    int sum;
    int main(){
    	memset(dp,128,sizeof(dp));
    	dp[0][0][0][1]=0;
        n=read(),k=read();
        for(int i=1;i<=n;i++) cin>>s[i];
        for(int i=1;i<=n;i++)
    	{
            for(int j=0;j<=k;j++)
    		{
                for(int f=0;f<=k;f++)
    			{
                    if(s[i]=='j')
    				{
                        dp[i][j][f][0]=max(dp[i-1][j][f][0],dp[i-1][j][f][1]);                          
                        if(j!=0) dp[i][j][f][1]=max(dp[i-1][j-1][f][0]+1,dp[i-1][j-1][f][1]); 	 	    
                    }
    				if(s[i]=='z')
    				{
                        dp[i][j][f][1]=max(dp[i-1][j][f][0]+1,dp[i-1][j][f][1]);				
                        if(f!=0) dp[i][j][f][0]=max(dp[i-1][j][f-1][0],dp[i-1][j][f-1][1]);       
                    }
                }
            }
        }
        for(int i=0;i<=k;i++) sum=max(sum,max(dp[n][i][i][0],dp[n][i][i][1]));
        cout<<sum;
    }
    
    

    分析:子序列问题用到next数组

    nxt[i][j]表示i位置后面第一个字符j的位置

    剩下的就是直接贪心找即可

    void slove() {
        cin >> s >> t;
        s = "?" + s; t = "?" + t;
        for (int i = 0; i < 26; i++)id[i] = -1;
        for (int i = s.length()-1; i >= 0; i--) {
            for (int j = 0; j < 26; j++)nxt[i][j] = id[j];
            if (i)id[s[i] - 'a'] = i;
        }
        int ans = 0;
        int j = 1;
        while (j < t.length()) {
            if (nxt[0][t[j] - 'a'] == -1) { cout << "-1" << endl; return; }
            ans++;
            int u = 0;
            while (j < t.length() && nxt[u][t[j] - 'a'] != -1) {
                u = nxt[u][t[j] - 'a'];
                j++;
            }
        }
        cout << ans << endl;
    }
    
  • 相关阅读:
    layui的模块化和非模块化使用
    layui实现类似于bootstrap的模态框功能
    ajax下载文件
    【IDEA】IDEA中maven项目pom.xml依赖不生效解决
    主-主数据库系统架构
    MyEclipse x.x各版本终极优化配置指南
    Cactus入门
    有史以来最出彩的编程语言名字
    安卓开发20:动画之Animation 详细使用-主要通过java代码实现动画效果
    第一次讲课总结
  • 原文地址:https://www.cnblogs.com/wzxbeliever/p/16466785.html
Copyright © 2020-2023  润新知