第14周 数据结构-字符串

本文探讨了字符串在数据结构中的应用,包括回文子字符串的计数、二进制子串统计、子序列计数以及最短回文字符串的构造。通过动态规划和特殊算法如KMP解决相关问题,涉及字符串翻转、同构判断和回文判断等核心概念。
摘要由CSDN通过智能技术生成

数据结构-字符串

字符串翻转

字符串循环移位包含、字符串循环移位、字符串中单词的翻转

两个字符串包含的字符是否完全相同

s = "anagram", t = "nagaram", return true.
s = "rat", t = "car", return false.

计算一组字符集合可以组成的回文字符串的最大长度

Input : "abccccdd"
Output : 7
Explanation : One longest palindrome that can be built is "dccaccd", whose length is 7.

字符串同构

Given "egg", "add", return true.
Given "foo", "bar", return false.
Given "paper", "title", return true.

回文子字符串个数

class Solution {
public:
    int countSubstrings(string s) {
        /*
            思路:使用动态规划的思路,字符串s[i:j]是否为回文字符串取决于s[i+1:j-1]和s[i]==s[j]的结果
            时间复杂度:O(n^2)
            空间复杂度:O(n^2)
        */
        int dp[1005][1005];
        memset(dp, 0, sizeof(dp));
        
        int ans = 0;
        for(int diff = 0; diff < s.size(); diff++){
            for(int i = 0;i+diff < s.size(); i++){
                int j = i+diff;
                if(i == j)
                    dp[i][j] = 1;
                else if(s[i] == s[j]){
                    if(j == i+1)
                        dp[i][j] = 1;
                    else
                        dp[i][j] = dp[i+1][j-1];
                }
                ans += dp[i][j];
            }
        }
        return ans;
    }
};

统计二进制字符串中连续 1 和连续 0 数量相同的子字符串个数

class Solution {
public:
    int countBinarySubstrings(string s) {
        /*
        思路:遇到01或者10开始向左右延伸,计算目标字符串的个数
        时间复杂度:O(n^2)
        空间复杂度:O(1)
        */
        int n = s.size();
        int ans = 0;
        
        for(int i = 0;i < n-1; i++){
            if(s[i] != s[i+1]){
                int left = i, right = i+1;
                while(left >= 0 && right < n && s[left] == s[i] && s[right] == s[i+1]){
                    ans += 1;
                    left -= 1;
                    right += 1;
                }
            }
        }
        
        return ans;
    }
};

子序列计数

典型的动态规划,在s中找t,定义dp[i][j]为在s[:j]中找到t[:i]的个数

class Solution {
public:
    int numDistinct(string s, string t) {
        if(t.size() > s.size())
            return 0;
        
        long long unsigned dp[1005][1005];
        memset(dp, 0, sizeof(dp));
        
        for(int i = 1;i <= t.size(); i++){
            for(int j = 1;j <= s.size(); j++){
                if(i == 1 && j == 1)
                    dp[i][j] = (t[i-1] == s[j-1]);
                if(t[i-1] == s[j-1]){
                    if(i == 1)
                        dp[i][j] = dp[i][j-1] + 1;    
                    else 
                        dp[i][j] = dp[i][j-1] + dp[i-1][j-1];
                }
                else
                    dp[i][j] = dp[i][j-1];
                //cout << dp[i][j] << " " << t[i-1] << s[j-1];
            }
            //cout << endl;
        }
        
        return dp[t.size()][s.size()];
    }
};

最短的回文字符串

解题思路:本题要求是在s的左边填充字符使得填充后的字符串为回文字符串,要求输出满足要求的最短的字符串。本题的求解目标可转化为求s从左边开始最长的回文子字符串,假设为s[:k],则只需要在左边填充s[k+1:]反序后的结果即为所求的回文字符串,为什么s从左边开始最长的回文字符串即为所求,可用反证法证明。

现在问题是怎么求解s从左边开始的最长的回文字符串s[:k],一种简单的方法就是从左到右扫描,时间复杂度O(n^2),不满足要求,虽然表面上问题是回文串的判断,但事实上是字符串匹配问题,只是匹配的源字符串和目标字符串在一个字符串中,所以就想到使用KMP算法,KMP算法具体介绍见链接,回文字符串的判定需要对KMP算法进行一点改动,具体见代码。

class Solution {
public:
    bool check(string s, int i, int j){
        while(i < j && s[i] == s[j]){
            i += 1;
            j -= 1;
        }
        return i >= j;
    }
    
    string shortestPalindrome(string s) {
        if(s.size() == 0)
            return s;
        
        // O(n^2)找从左边开始的最长的回文子串
        // for(int j = s.size()-1; j >= 0; j -= 1){
        //     if(check(s, 0, j)){
        //         maxIndex = j+1;
        //         break;
        //     }
        // }
        
        // O(n)找从左边开始的最长的回文子串
        vector<int> next_arr(s.size(), 0);
        for(int i = 1;i < s.size(); i++){
            if(s[i] == s[next_arr[i-1]])
                next_arr[i] = next_arr[i-1] + 1;
            else{
                int tmp = next_arr[i-1];
                while(s[i] != s[tmp] && tmp != 0)
                    tmp = next_arr[tmp-1];
                next_arr[i] = tmp == 0 ? (s[i] == s[0]) : tmp;
            }
        }
        
        int i = 0, j = s.size()-1;
        int maxIndex = 1;
        while(j > 0){
            if(s[i] == s[j]){
                i += 1;
                j -= 1;
            }
            else if(i == 0)
                j -= 1;
            else { // i!=0
                i = next_arr[i-1];
            }
            
            if(i >= j){
                // cout << i << " " << j << endl;
                maxIndex = j + i + 1;
                break;
            }
        }
        
        string pad = s.substr(maxIndex, s.size()-maxIndex);
        i = 0;
        j = pad.size()-1;
        while(i < j){
            swap(pad[i], pad[j]);
            i += 1;
            j -= 1;
        }
        return pad + s;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值