c++字符串、数组算法题

本文探讨了C++中的字符串操作,包括最长子串、子序列问题的解题策略,如最长递增子串、字符串排序等。同时,文章还讲解了数组的算法应用,如局部极小值、寻找特定正整数、最大元素距离等,通过实例深入解析了字符串和数组的各种操作技巧和算法思想。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

文章目录

字符串(String)

专题:关于各种最长子串、子序列问题

300.求最长子序列

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        if(nums.empty())return 0;
        int len = nums.size();
        vector<int> dp(len,1);
        for(int i =1 ;i<len ;i++)
        {
            int temp = 0;
            for(int j =0 ;j<i ;j++)
            {
                if(nums[j]<nums[i]) temp = max(temp,dp[j]);
            }
            dp[i] = temp + 1;
        }
        return *max_element(dp.begin(),dp.end());
    }
};

俄罗斯套娃问题就是再这个基础之上做的

class Solution {
public:
    static inline bool func(const vector<int>& a, const vector<int>& b)
    {
        if(a[0]<b[0])return true;
        if(a[0]==b[0] && a[1]>b[1])return true;
        return false;
    }

    int maxEnvelopes(vector<vector<int>>& envelopes) {
        if(envelopes.empty())return 0;
        sort(envelopes.begin(),envelopes.end(),func);
        int len = envelopes.size();
        vector<int> dp(len,1);
        for(int i = 1 ;i< len ;i++)
        {
            int temp = 0;
            for(int j = 0; j<i; j++)
            {
                if(envelopes[j][1]<envelopes[i][1])temp =max(temp,dp[j]);
            }
            dp[i] = temp + 1;
        }
        return *max_element(dp.begin(), dp.end());
    }
};

674.最长递增子串


class Solution {
public:
    int findLengthOfLCIS(vector<int>& nums) {
        int n = nums.size();
        if(n<=1) return n;
        int res = 1, len=1;
        for(int i=1; i<n; i++){
            if(nums[i] > nums[i-1]) len+=1;
            else len = 1;
            res = max(len, res);//进行比较
        }
        return res;
    }

std::string 可更改,也可以用[]提取字符,和char[]数组使用上区别不大;

size

专门针对stl里用的,char字符串数组用strlen()

substr方法:

substr(n,m) n为起点,m要注意是长度而不是结束点!

erase方法

erase(n,m) n为起点,m要注意是长度而不是结束点!

find方法

find(‘a’)寻找字符a,并返回找到位置,没找到返回-1
find(’’,n)从n开始找

一个疑问????

	string s = "ajoibaivobaoibjoai";
	//int len = s.size();
	int n = s.find('a'); //l = 0
	//cout << typeid(l).name() << endl;
	bool k1 = (-1 < n );//正确
	bool k2 = (-1 < s.find('a'));//错误

n和s.find(‘a’)明明是相等的,为什么将整个放入就不对了??必须定义这一个才行??

字符范围

8位
[-128.127]通常转换为unsigned [0,+255]

题目类型

字典序;
简单操作:插入删除旋转;
规则判断(罗马数字判断、整数合法、浮点数);
数字运算(大数加法、二进制加法);
排序交换(partition过程);
字符计数(hash)变位词;
匹配(正则表达式、全串匹配、KMP、周期判断);
动态规划(LCS、编辑距离、最长回文串);
搜索(单词变换、排列组合);

例一:把一个0-1串进行排序,可以交换任意连个位置,最少交换的次数?

int main()
{
	string s = "011001010100101010";
	int len = s.size();
	int i = 0;
	int j = len - 1;
	int num = 0;//维护这个对象

	//s[i];
	//s[j];
	//bool a = (s[i] == '0' );
	while (i < j)
	{
		if (s[i] == '1' && s[j] == '0')
		{
			swap(s[i], s[j]);
			num = num + 1;
			i = i + 1;
			continue;
		}
		else if (s[i] == '1' && s[j] == '1')
		{
			j = j - 1;
			continue;
		}
		else if (s[i] == '0' && s[j] == '1')
		{
			i = i + 1;
			j = j - 1;
			continue;
		}
		else if (s[i] == '0' && s[j] == '0')
		{
			i = i + 1;
			continue;
		}

	}
	
	std::cout << num << endl;
	//std::cout << 1 << endl;
	return num;

	//
}

使用了快排的思想,一个在左一个在右,要特别注意的一点是,从字符串取出的类型是字符,所以比较的时候要加‘0’和‘1’;
线性复杂度O(len)

例二:删除一个字符串所有的a,并且复制所有的b。注:字符数组足够大(就在字符串上修改)


int main()
{

	string s = "ajoibaivobaoibjoai";
	
	int n1 = s.find('a');
	while (n1 > -1)
	{
		s.erase(n1,1);
		n1 = s.find('a');
	}
	int len = s.size();
	int m = 0;
	int num = 0;
	int n2 = s.find('b',m);
	//int m = 0;
	while (n2 > -1 && m < len)
	{
		num = num + 1;
		m = n2 + 1;
		n2 = s.find('b',m);
	}
	for (int i = 0 ; i < num ; i++)
	{
		s.push_back('b');
	}

	cout << s << endl;
	return 1;


	//
}

1.首先要能够正确使用erase和push_back两个方法;
2.在对字符串进行迭代过程中一定不能删除元素,不然会造成下表混乱
3.布尔类型要小心使用,多定义变量

例三:一个字符串只包含*和数字,请把它的*号都放开头

	string s = "25478*798**698*98";
	int len = s.size();
	int n = s.find('*');
	int num = 0;
	int i = 0;
	int j = len - 1;
	while (i < j)
	{
		if (!isdigit(s[i]) && !isdigit(s[j])) { i = i + 1; }
		if (!isdigit(s[i]) && isdigit(s[j])) { i = i + 1; j = j - 1; }
		if (isdigit(s[i]) && !isdigit(s[j])) { swap(s[i], s[j]); num = num + 1; i = i + 1; j = j - 1; }
		if (isdigit(s[i]) && isdigit(s[j])) { j = j - 1; }
	}
	cout << s << endl;
	cout << num << endl;

	return 1;

这是数字改变的情况,如果要求数字不变,则先遍历取的*的个数以及位置,再重新构造数组。

例四:给定两个串a和b,问b是否是a的子串的变位词。例如a =“hello”,b=“lel”,“lle”,"ello"都可以,但"elo"不行。

做法一:

	string a = "affagahahhafhahah";
	//string b = "opl";
	string b = "ahf";

	//当n=2则有2中可能,当n=3则有6种,n有n的阶乘种
	set<char> s1;
	int lena = a.size();
	int lenb = b.size();
	for (auto i : b)
	{
		s1.insert(i);
	}

	for (int j = 0; j < (lena - lenb); j++)
	{
		set<char> s2;
		for (int c = 0; c < lenb; c++)
		{
			s2.insert(a[j+c]);

		}
		if (s1 == s2)
		{
			cout << "true" << endl;
			return 1;
		}

	}
	cout << "false" << endl;

//两个思路:思路1:求出字符串的所有变位词,然后形成集合,看b是否是,pass;思路2:求出b的所有情况,去a查找
//实时证明上面两种都极难写,而且是n方复杂度,写不出来就只能转变思路,用滑动窗口滑a,取出来的只要字符与b相同就可以!

做法二:哈希

	string a = "affagahahhafhahah";
	//string b = "opl";
	string b = "ahf";

	//哈希:26个英文字母对应一个长度26的哈希表,
	int arr1[26] = {};
	for (auto i : b)
	{
		arr1[i - 'a'] = arr1[i - 'a'] + 1;

	}
	int lena = a.size();
	int lenb = b.size();
	int num = 0;
	for (int j = 0; j < lena - lenb; j++)
	//int j = 10;
	
	{
		int arr2[26] = {};
		for (int c = 0; c < lenb; c++)
		{
			arr2[a[j + c] - 'a'] = arr2[a[j + c] - 'a'] + 1;
		}
		//if (arr1[] == arr2[])
		//{
		//	cout << "True" << endl;
		//}
		num = 0;
		for (int v = 0; v < 16; v++)
		{
			if (arr1[v] == arr2[v])
			{
				num = num + 1;
			}
			//num = num + 1;
		}
		if (num == 16) 
		{
			cout << "True" << endl; 
			return 1; 
		}

	}
	cout << "False" << endl;
	return 1;

注意几点:

  1. ’b’- ‘a’ =1可以直接实现字母到数字的映射,这使哈希表的建立变得简单;
  2. 比较两个数字型的数组相等时,一定不能直接比较数组名字,因为名字代表的是首地址!对于数字型的只能去循环遍历,字符型的可以考虑strump函数。
  3. 滑动窗口和哈希表映射这种思想还是值得留意的

例五:单词翻转:翻转句子中的全部单词,单次内容不变。如"I am a student.“变为"student.a am I”

	string s = "I am a student.";
	//string s2 = "";
	//string s3 = s2 + s.substr(2,2);
	
	bool n;
	int lens = s.size();
	vector<int> myvec;
	for (int i = 0;i<lens;i++)
	{
		n = isalpha(s[i]);
		if (n == 0)
		{
			myvec.push_back(i);
			continue;
		}
	}
	string s1 = "";
	vector<int>::iterator iter;
	int lenv = myvec.size();
	int a1 = 0;
	int a2 = 0;

	for (int j = 0; j < lenv-1; j++)
	{
		a1 = myvec.back();
		myvec.pop_back();
		a2 = myvec.back();
		s1 = s1 + s.substr(a2 +1,a1-a2);
	}
	a1 = myvec.back();
	myvec.pop_back();
	a2 = 0;
	s1 = s1 + s.substr(a2, a1-a2);
	cout << s1 << endl;



1.涉及到动态变化时不能用arr,容器vector和list在用法和方法上差不多;
2.截取字符串记得使用substr方法;
3.有时可以不使用迭代器而只是用Push——back等常见函数;
4.判断字母isalpha判断数字isdigit

例六:一个字符串,找出第一个不重复的字符,如“abbbabcd”,则第一个不重复就是c

使用哈希的思想,建立256个bool数组array,初始都为false,从头开始扫描字符串,扫到一个,将以其ascii码为下标的元素置true。例如扫描到A的时候,执行:array[‘A’]=true。第二边扫描,扫到一个字母就以其ascii码为下标,去array数组中看其值,如果是true,返回改字母,如果是false,继续扫描下一个字母。

链接:https://www.nowcoder.com/questionTerminal/c979e5876acb4c05b22c85c89a347b5a
来源:牛客网

char firstNonDuplicate(std::string s) {
    std::unordered_map<char, int> m;
    for(auto c : s) ++m[c];
    for(auto c : s) {
        if(m[c] == 1) return c;
    }
    return 0; // '\0'
}

关于哈希表:
https://blog.csdn.net/Huang_JinXin/article/details/95785544
1.STL哈希表使用unordered_map
2.循环便利 for(auto i 😒)
3.++m[c],如果没有则创建,且初值为1,如果有则加1

数组

STL vector, [];
vector自动扩容;
数组下表做哈希计数;
数组与map;
给数组顺序;

例一:局部极小值:一个给定的不包含相同元素的整数数组,每个局部极小值的定义是一个值比左右相邻的都小的值,求一个局部极小值

	vector<int> myvec = {3,5,2,5,23,4};
	vector<int>::iterator iter;
	int lenv = myvec.size();
	for (auto iter = myvec.begin()+1; iter != myvec.end()-3; iter++)
	{
		if (*iter > *(iter + 1) && *(iter + 2) > *(iter + 1))
		{
			cout << *(iter+1) << endl;
			return 1;
		}
	}

注意迭代器的定义使用和容器元素的提取

例二:给一个数组,找到从1开始第一个不在里面的正整数


//快速排序(从小到大)
void quickSort(int left, int right, vector<int>& arr)//这里的参数必须定义成引用类型才行,如果不传递引用,会生成一个同实参一样大的vector<int>对象。且不能修改实参的值
void quicksort(int left, int right, vector<int> &arr)
{
	if (left >= right) { return; }
	int i = left;
	int j = right;
	while (i < j)
	{
		while (arr[j] >= arr[left] && i < j) { j--; }
		while (arr[i] <= arr[left] && i<j) { i++; }
		
		if (i < j) { swap(arr[i], arr[j]); }
	}
	swap(arr[i], arr[left]);

	quicksort(left, i-1,arr );//留意
	quicksort(i+1, right,arr);
}

原文链接:https://blog.csdn.net/qq_28584889/article/details/88136498

学习快速排序

	vector<int> myvec = {14,5,1,5,146,46,16,6,1,4,1,412,2,3,34,5,66,56};
	//vector<int> myvec = {7,6,4};
	//方法一:对于每一个正整数取遍历,pass
	//方法二:先对数组排序,使用最快的排序方式
	int lenv = myvec.size();

	vector<int>::iterator i;
	//vector<int>::iterator j;
	
	int left = 0;
	int right = lenv - 1;
	quicksort(left,right,myvec);
	//i = myvec.begin();
	//int n = *i;
	//int m = *(i+1);
	//bool b1 = *(i + 1) != *i;
	//bool b2 = *(i + 1) != *i + 1;
	//bool b = (*(i + 1) != *i) || (*(i + 1) != *i + 1);


	for (i = myvec.begin(); i != myvec.end()-1; i++)
	{
		cout << *i << endl;
		if (*(myvec.begin()) != 1)
		{
			cout << "asdfasdfa   1" << endl;
			return 1;
		}
		else
		{
			if ((*(i + 1) != *i) && (*(i + 1) != *i + 1))
			{
				cout<<"fsafsdfaaf   "<<*(i)+1<<endl;
				return 1;
			}

			
				
		}
	}
	cout << myvec.back() + 1 << endl;

例三:元素最大距离:给定一个整数数组,求把这些整数表示在数轴上,相邻两个数差的最大值

	vector<int> myvec = {14,5,1,5,146,46,16,6,1,4,1,412,2,3,34,5,66,56};
	//vector<int> myvec = {7,6,4};
	//方法一:对于每一个正整数取遍历,pass
	//方法二:先对数组排序,使用最快的排序方式
	int lenv = myvec.size();

	vector<int>::iterator i;
	//vector<int>::iterator j;
	
	int left = 0;
	int right = lenv - 1;
	quicksort(left,right,myvec);


	int max = 0;
	for (i = myvec.begin(); i != myvec.end() - 1; i++)
	{
		cout << *i << endl;
		//max = *(i + 1) - *i;
		if (max < (*(i + 1) - *i))
		{
			max = *(i + 1) - *i;
		}
	}
	cout <<"max=" <<max << endl;

例四:只出现一次的数:一个数组,所有数字都出现了两次,只有两个数出现了一次,求着两个数。

	//vector<int> myvec = {14,5,1,5,146,46,16,6,1,4,1,412,2,3,34,5,66,56};
	vector<int> myvec = {7,5,7,6,2,6,2,8};
	//方法一:对于每一个正整数取遍历,pass
	//方法二:先对数组排序,使用最快的排序方式
	int lenv = myvec.size();

	vector<int>::iterator i;
	//vector<int>::iterator j;
	//int n = *(myvec.end()-1);

	int left = 0;
	int right = lenv - 1;
	quicksort(left,right,myvec);


	vector<int> res;
	for (i = myvec.begin()+1; i != myvec.end() - 1; i++)
	{
		//cout << *i << endl;
		if ((*(i - 1) != *i) && (*i != *(i + 1)))
		{
			res.push_back(*i);
		}


	}
	if (*(myvec.begin()) != *(myvec.begin()+1)) { res.push_back(*(myvec.begin())); }
	if (*(myvec.end()-1) != *(myvec.end()-2)) { res.push_back(*(myvec.end()-1)); }
	for (vector<int>::iterator r = res.begin(); r != res.end(); r++)
	{
		cout<<*r<<endl;
	}

要注意迭代器的end()不指向任何内容

例五:找出超过一半的数

	//vector<int> myvec = {14,5,1,5,146,46,16,6,1,4,1,412,2,3,34,5,66,56};
	vector<int> myvec = {7,5,7,6,2,6,2,8};
	//方法一:对于每一个正整数取遍历,pass
	//方法二:先对数组排序,使用最快的排序方式
	int lenv = myvec.size();

	//int n = myvec[3];

	vector<int>::iterator i;
	//vector<int>::iterator j;
	//int n = *(myvec.end()-1);

	int left = 0;
	int right = lenv - 1;
	quicksort(left,right,myvec);

	
	if ((lenv % 2) == 0)
	{
		cout << myvec[lenv/2] << endl;
	}
	else
	{
		cout<<myvec[(lenv-1)/2]<<endl;
	}
	//for (i = myvec.begin() + 1; i != myvec.end() - 1; i++)
	//{
	//
	//}

还可以用哈希做

例六:给定浮点数组a,求一个数组b,b[i] = a[0]*a[1]…a[i-1]*a[i+1]…a[n-1],不能使用除法,不能再开数组。

//vector<int> myvec = {14,5,1,5,146,46,16,6,1,4,1,412,2,3,34,5,66,56};
vector<int> a = {7,5,7,6};
//方法一:对于每一个正整数取遍历,pass
//方法二:先对数组排序,使用最快的排序方式
int lena = a.size();

//int n = myvec[3];

vector<int>::iterator i;
//vector<int>::iterator j;
//int n = *(myvec.end()-1);

//int left = 0;
//int right = lenv - 1;
//quicksort(left,right,myvec);
vector<int> b;
for (int c = 0; c < lena; c++)
{
	int cheng = 1;
	if (c == 0) 
	{ 
		for (i = a.begin() + 1; i != a.end(); i++)
		{
			cheng = cheng * (*i);
		}
		b.push_back(cheng); 
	}
	else if (c == lena -1)
	{
		for (i = a.begin() ; i != a.end()-1; i++)
		{
			cheng = cheng * (*i);
		}
		b.push_back(cheng);
	}
	else
	{
		for (i = a.begin(); i != a.begin() + c; i++)
		{
			cheng = cheng * (*i);
		}
		for (i = a.begin() + c + 1; i != a.end(); i++)
		{
			cheng = cheng * (*i);
		}
		b.push_back(cheng);
	}

}
vector<int>::iterator j;
for (j = b.begin(); j != b.end(); j++)
{
	cout<<*j<<endl;
}

例七:n个整数的无序数组,找到每个元素后面比它大的第一个数,要求时间复杂度为O(N)

借助一个栈(s),栈中的每个元素代表未找到右侧第一个大于当前元素的值的下标(比较拗口,就是还没确定右边谁比它大,把这个元素的下标放到栈中)。

  1. 先两两比较相邻的两个元素值(a, b),如果是a>=b,说明元素是逆序排列的,将a的下标压栈,继续前行

  2. 一直找到a<b的元素,b即为第一个大于a的元素,将a下标出栈。

  3. 检查栈中的每个值指向的元素c,如果c<b,说明b即为第一个大于c的元素,将c下标出栈。一直到栈为空或者c>=b为止。

vector<int>  FindMax(vector<int> &num)
{
    int len=num.size();
    if(len==0) return {};    //空数组,返回空
    vector<int> res(len,-1);    //返回结果:初始化-1,表示未找到
    stack<int> notFind; //栈:num中还未找到符合条件的元素索引

    int i=0;
    while(i<len)    //遍历数组
    {
         //如果栈空或者当前num元素不大于栈顶,将当前元素压栈,索引后移
        if(notFind.empty() || num[notFind.top()]>=num[i])
        {
            notFind.push(i++);
        }
       //有待处理元素,且num当前元素大于栈顶索引元素,符合条件,更新结果数组中该索引的值,栈顶出栈。
        else
        {
            res[notFind.top()]=num[i];
            notFind.pop();
        } 
    }
 return res;
}

一个数组中有若干正整数,将此数组划分为两个子数组,使得两个子数组各元素之和a,b的差最小,对于非法输入应该输出ERROR。

dfs法超时

void dfs(vector<int>& vt, vector<int>& path, int sum,int target,bool flag,int m,vector<int>& record)
{
    if(flag)return;
    if(sum==target/2){flag = true;record = path;return;}
    if(sum>target/2)
    {
        if(abs(sum-target/2)<m){m = abs(sum-target/2);record = path;}
        return;
    }
    for(int i = 0;i<vt.size();i++)
    {
        path.push_back(vt[i]);
        sum += vt[i];
        dfs(vt,path,sum,target,flag,m,record);
        sum -= vt[i];
        path.pop_back();
    }
}

int main()
{
    string s;
    while(getline(cin,s))
    {
        int len = s.size();
        bool b = true;
        for(int i = 0;i<s.size();i++)
        {
            if(isdigit(s[i])==false && s[i]!=' ')
            {b= false; break;}
        }
        if(!b){cout<<"ERROR"<<endl;continue;}
        vector<int> vt;
        int i =0;
        int target = 0;
		while (i < s.size())
		{
			if (s[i] == ' ') { i++; continue; }
		
            else
            {
				int temp = 0; 
                while(i<s.size() && isdigit(s[i]))
                {
                    temp = temp*10 + int(s[i]-'0');i++;
                }
                target += temp;
                vt.push_back(temp);
            }
        }
        vector<int> path,record;
        bool flag = false;
        dfs(vt,path,0,target,flag,INT_MAX,record);
        int res = 0;
        for(auto i:record){res+=i;}
        cout<<res<<" "<<target-res<<endl;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值