《剑指2》第3章 字符串

一、基本知识

1.双指针

通常可以用两个指针来判断一个字符串是不是回文,要么两个指针从字符串的两端开始向中间移动,要么两个指针从中间开始向两端移动。

2.哈希表:

如果题目强调字符串只包含英文小写字母,可以使用数组模拟一个简单的哈希表。

3.变位词:指组成各个单词的字母及每个字母出现的次数完全相同,只是字母排列的顺序不同。

通常可以用一个哈希表来统计每个字符出现的次数,有了哈希表就很容易判断两个字符串是不是一组变位词。

二、题目

1.字符串中的变位词

思路:哈希表+双指针(当哈希表中的值都为0时为true)

tips:在函数中无法获取数组长度,使用sizeof只能获取形参的大小,要实现获得数组长度,需要在函数中增加参数。

bool allZero(int* arr, int arrSize);

bool checkInclusion(char * s1, char * s2){
    int s1Len = strlen(s1);
    int s2Len = strlen(s2);
    if(s2Len<s1Len)
        return false;
    int hash[26] = {0};
    for(int i=0;i<s1Len;i++)
    {
        hash[s1[i]-'a']++;
        hash[s2[i]-'a']--;
    }
    if(allZero(hash,26))
        return true;

    for(int i = s1Len;i<s2Len;i++)
    {
        hash[s2[i] - 'a']--;
        hash[s2[i-s1Len] - 'a']++;
        if(allZero(hash,26))
            return true;
    }

    return false;
}

bool allZero(int* arr, int arrSize)
{
    for(int i=0;i<arrSize;i++)
    {
        if(arr[i] != 0)
            return false;
    }
    return true;
}

2.字符串中的所有变位词

思路:用字符串s1构造哈希表,之后用双指针在s2中移动,判断子串是否符合题意。

头脑风暴:尝试使用使用布尔值构造哈希表,失败,因为会出现子串中出现两个d,指针移走后边上的d移走变为false,但是子串中仍存在d,应该仍为true。就是说不能用简单的true和false记录,哈希表应该记录出现个数。

bool allZero(int* arr, int arrSize);
int* findAnagrams(char * s, char * p, int* returnSize){
    int sLen = strlen(s);
    int pLen = strlen(p);
    int hash[26] = {0};
    int* ret = (int*)malloc(sLen*sizeof(int));
    int retLen = 0;
    memset(ret,0,sLen*sizeof(int));
    if(sLen < pLen)
    {
        *returnSize = 0;
        return NULL;
    }
        
    for(int i=0;i<pLen;i++)
    {
        hash[p[i] - 'a']++;
        hash[s[i] - 'a']--;
    }
    if(allZero(hash,26))
        ret[retLen++] = 0;
    for(int i=pLen;i<sLen;i++)
    {
        hash[s[i] - 'a']--;
        hash[s[i-pLen] - 'a']++;
        if(allZero(hash,26))
            ret[retLen++] = i-pLen+1;
    }
    *returnSize = retLen;
    return ret;
}

bool allZero(int* arr, int arrSize)
{
    for(int i=0;i<arrSize;i++)
    {
        if(arr[i] != 0)
            return false;
    }
    return true;
}

3.不含重复字符的最长子字符串

思路:哈希表+滑窗

int lengthOfLongestSubstring(char * s){
    if(s == NULL)
        return NULL;
    int hash[256] = {0};
    int len = strlen(s);
    int left = 0;
    int ret = 0;
    for(int right = 0;right<len;right++)
    {
        hash[s[right]]++;
        while(left<=right && hash[s[right]] == 2)
        {
            hash[s[left]]--;
            left++;
        }
        ret = fmax(ret,right-left+1);
    }
    return ret;
}

4.包含所有字符的最短字符串

思路:双哈希表,当s的哈希值大于t的哈希值时左指针移动

char * minWindow(char * s, char * t){
    int sLen = strlen(s);
    int tLen = strlen(t);
    if(sLen<tLen)
        return "";
    int tHash[256] = {0};
    int sHash[256] = {0};
    char* ret = (char*)malloc((sLen+1) * sizeof(char));
    int left = 0;
    int cnt = 0;
    int minbegin = 0;
    int minend = sLen;
    for(int i=0;i<tLen;i++)
    {
        tHash[t[i]]++;
    }
    for(int right=0;right<sLen;right++)
    {
        sHash[s[right]]++;
        if(sHash[s[right]] <= tHash[s[right]])
            cnt++;
        while(left<=right && sHash[s[left]] > tHash[s[left]])
        {
            sHash[s[left]]--;
            left++;
        }
        if(cnt == tLen && right-left+1 < minend-minbegin+1)
        {
            minend = right;
            minbegin = left;
        }
    }
    int retLen = minend-minbegin+1;
    if(retLen == sLen+1)
        return "";
    memcpy(ret,&s[minbegin],retLen);
    ret[retLen] = '\0';
    return ret;
}

5.有效回文

知识:

isalnum() 函数用来检测一个字符是否是字母或者十进制数字。

如果仅仅检测一个字符是否是字母,可以使用 isalpha() 函数;

如果仅仅检测一个字符是否是十进制数字,可以使用 isdigit() 函数。

tolower()函数:将大写字母转换为小写字母

toupper()将小写字母转换为大写字母

注意:为了防止指针错过或者数组越界,while判断使用的是if-elseif而不都是if

bool isPalindrome(char * s){
    if(s == NULL)
        return false;
    int left = 0;
    int right = strlen(s) - 1;
    while(left <= right)
    {
        if(!isalnum(s[left]))
            left++;
        else if(!isalnum(s[right]))
            right--;
        else if(tolower(s[left]) == tolower(s[right]))
        {
            left++;
            right--;
        }
        else
            return false;
    }
    return true;
}

6.最多删除一个字符得到回文

思路:由于实现不知道应该删除两个不同字符中的哪一个,因此两个字符都可以进行尝试

bool isPalindrome(char* s,int left,int right);
bool validPalindrome(char * s){
    if(s == NULL)
        return false;
    int sLen = strlen(s);
    int left = 0;
    int right = sLen-1;
    while(left<=right)
    {
        if(s[left] == s[right])
        {
            left++;
            right--;
        }
        else
            return isPalindrome(s,left+1,right) | isPalindrome(s,left,right-1);
    }
    return true;
}

bool isPalindrome(char* s,int left,int right)
{
    while(left<=right)
    {
        if(s[left] == s[right])
        {
            left++;
            right--;
        }
        else
            return false;
    }
    return true;
}

7.回文字符串的个数

思路一:中心拓展(考虑奇偶)

思路二:马拉车 算法详解

中心扩展法的实现(马拉车太难了呜呜呜)

int countPalindrome(char* s,int start,int end,int len);
int countSubstrings(char * s){
    int len = strlen(s);
    int ret = 0;
    for(int i=0;i<len;i++)
    {
        ret += countPalindrome(s,i,i,len);
        ret += countPalindrome(s,i,i+1,len);
    }
    return ret;
}

int countPalindrome(char* s,int start,int end,int len)
{
    int count = 0;
    while(start>=0 && end<len && s[start] == s[end])
    {
        start--;
        end++;
        count++;
    }
    return count;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值