字符串相关算法(0-1交换、字符替换和复制、交换星号、子串变位词、单词翻转 )

0-1交换
  • 把一个0-1串(只包含0和1的串)进行排序,你
    可以交换任意两个位置,问最少交换的次数
分析: 快排partition?最左边的那些0和最右边的

那些1都可以不管

  • o 00…0001…….0111….1
伪代码实现
void exchangeo1{
    int answer = 0;
    for(int i=-,j=len-1;i<j;++i,--j){
        for(;(i<j)&&(a[i]=='0');++i);
        for(;(j>i)&&(a[j]=='');--j);
        if(i<j) ++answer;
    }
}
字符替换和复制
  • 删除一个字符串所有的a,并且复制所有的b。注:字符数
    组足够大
分析
  • 先删除a,可以利用原来字符串的空间
 int n = 0, numb = 0;
 for(int i=0;s[i];++i){
     if(s[i]!='a'){s[n++]=s[i];}
     if(s[i]=='b'){++numb;}
 }
s[n]=0;
  • 再复制b,注意字符串要加长
    • 先计算字符串里有几个b,得到复制后的长度
    • 然后“倒着”复制——惯用技巧
代码实现
int newLength = n+numb;
s[newLength] = 0;
for(int i=newLength,j=n-1;j>=0;--j){
    s[i--] = s[j];
    if(s[j]=='b')s[i--]='b';
}
交换星号
  • 一个字符串只包含和数字,请把它的
    号都放开头。
分析
方法1 快排partition——数字相对顺序会变化
  • 循环不变式: [0…i – 1]都是*, [i…j – 1]是数字,[j…n –
    1]未探测
  • 代码实现
for(int i=0,j=0;j<n;++j){
    if(s[j]=='*')swap(s[i++],s[j])
}
  • 实现样例(样例 012*4)
    • i=0, j = 0, 012*4 交换s[0],不变,i = 1
    • i=1, j = 1, 012*4 不变
    • i = 1, j = 2, 012*4 不变
    • i=1, j = 3, 交换s[1],s[3]变为 **102*4 并且i = 2
    • i=2, j = 4,**102*4不变
    • i = 2, j = 5, 交换s[2],s[5]变为***0214 且i = 3
    • 再往后没变化了
方法2 数字相对顺序不变
  • “倒着”
  • 代码实现
int j = n-1
for(int i=n-1;i>+0;--i)
    if(isdigist(s[i]))s[j--]=s[i];
for(;j>=0;--j)s[j]='*'
子串变位词
  • 给定两个串a和b,问b是否是a的子串的变位词。例如输入a = hello, b = lel, lle, ello都是
    true,但是b = elo是false。
    • 滑动窗口的思想
      • 动态维护一个“窗口”。
      • 比如b的长度是3,我们考察a[0…2], [1…3],[2…4]是否和b是变位词
      • 如何与b比较?
  • 我们用一个hash,基于字符串的特殊性,我们可以用[0…255]或者[0…65535]的数组,我们暂且认为它们都是小写英文字母,用[0…25]来表示b中每个单词出现多少次。
  • 我们可以存一下有多少个非0次出现的,以后有用
int nonzero
for(int i=0;i<lenb;++i)
    if(++num[b[i]-'a'==1])++nonZero;
  • 我们用b中的次数减去a中一个“窗口”内的字符种类,如果结果全是0,则找到这样的子串了。 注意num[]的含义变为了字符种类差
  • 第一个窗口 [0…lenb – 1] (注意lena < lenb无解)
for(int i=0;i<lenb;++i){
    int c = a[i] - 'a';
    --num[c];
    if(num[c]==0)--nonZero;
    else if(num[c]==-1)++nonZero;
}
if(nonZero == 0) return true;
  • 窗口如何滑动? 向右移动一位
    • 新窗口a[(i - lenb + 1)…i]
    • 旧窗口a[(i – lenb)… (i – 1)]
      • 扔掉a[i – lenb]
      • 加入a[i]
for(int i=lenb;i<lana;++i){
    int c = a[i-lenb] - 'a';
    ++num[c];
    if(num[c]==1)++nonZero;
    else if(num[c]==0) --nonZero;
    c = a[i] - 'a';
    --num[c];
    if(num[c]==0)--nonZero;
    else if(num[c]==-1)++nonZero;
    if(nonZero == 0) return true;    
}
单词翻转
  • 翻转句子中全部的单词,单词内容不变
    • 例如I’m a student. 变为student. a I’m
    • in-place翻转 字符串第i位到第j位
      • while (i < j) swap(s[i++], s[j–]);
    • 有什么用?
      • 翻转整个句子 : .tneduts a m’I
      • 每个单词单独翻转: student. a I’m
    • 难点? 如何区分单词?找空格,split
    • 思考题: 字符串循环移位abcd
      • 移动1次变为bcda
      • 移动2次变为cdab
      • 移动3次变为dabc
    • 结论: 长度为n, 移动m次,相当于移动m % n次
      • 前m % n位翻转, 后n – m % n位翻转
      • 总体再翻转一次 试验一下?

更多内容请关注微信公众号:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值