目录
引入
首先来看一道leetcode上的简单题目
题目描述
给定一个包含大写字母和小写字母的字符串,找到通过这些字母构造成的最长的回文串。
在构造过程中,请注意区分大小写。比如 "Aa"
不能当做一个回文字符串。
注意:
假设字符串的长度不会超过 1010。
示例 1:
输入:
"abccccdd"
输出:
7
解释:
我们可以构造的最长的回文串是"dccaccd", 它的长度是 7。
思路与题解
(复制自leetcode官方)
看懂了思路之后,就该根据思路实现代码了,在官方给出的C++答案中,代码是这样的:
class Solution {
public:
int longestPalindrome(string s) {
unordered_map<char, int> count;
int ans = 0;
for (char c : s)
++count[c];
for (auto p : count) {
int v = p.second;
ans += v / 2 * 2;
if (v % 2 == 1 and ans % 2 == 0)
++ans;
}
return ans;
}
};
在这段代码中,就使用到了两个C++11特性中新增的内容:auto类型说明符和无序容器unorder_map
那么这两个内容都有哪些特点与要点呢?
以下内容来自大学课本《C++ Primer》
auto类型
1、为C++11标准中新引入的一个类型说明符,可以让编译器为我们自动分析判断表达式所属的类型。
2、auto让编译器通过初始值来推算变量的类型(这一点类似于Python)。显然,auto定义的变量必须有初始值:
//由val1和val2相加的结果可以推断出item的类型 auto item = val1 + val2;//item初始化为val1和val2相加的结果
3、auto可以在一条语句中声明多个变量。因为一条生命语句只能有一个基本数据类型,所以该语句中所有变量的初始基本数据类型都必须一样。切记,符号&和*只从属于某个声明符,而非基本数据类型的一部分,因此初始值必须使同一种类型。
举几个例子(其中有《C++ Primer》原例)如下:
auto i = 0, *p = &i;//正确,i是整数,p是整形指针
auto sz = 0, pi = 3.14;//错误,sz和pi的类型不一致
auto str = '123', s = '234';//正确,str和s的类型相同
auto a = 123 + 234;//正确,此时a的数据类型为整型
在上述例子中,auto根据第一个表达式来确定整个语句的数据类型。
auto k = ci, &l = i;//k是整数,l是整型的引用
auto &m = ci,*p = &ci;//m是对整型的引用,p是指向整型常量的指针
auto &n = i, *p2= &ci;
上述例子的第三个例子是错误示例,读者朋友们能够看出原因吗?
除此之外,对于size函数来说,由于其返回的是一个string::size_type类型的值,不太容易理解和使用,所以可以使用auto类推断变量的类型:
auto len = line.size();//len的类型是string::size_type;
在我们熟知的for循环中,也可以利用auto,正如给出的题目例子中的for循环:
for (auto p : count) {
int v = p.second;
ans += v / 2 * 2;
if (v % 2 == 1 and ans % 2 == 0)
++ans;
}
由于count是一个无序容器unordered_map,所以在对其进行遍历时,可以使用auto来帮我们做这件事,这样倒也省下了不少力气。
auto也不止于此
以下含有大量原文,不喜欢的朋友可以跳过或参照原书
编译器推断出的auto类型有时候和初始值的类型并不完全一样,编译器会适当地改变结果类型使其更符合初始化规则。
首先,使用引用其实是使用引用的对象,特别是当引用被用作初始值时,真正参与初始化的其实是引用对象的值。此时编译器以引用对象的类型作为auto的类型:
int i = 0, &r = i; auto a = r;//a是一个整数(r是i的别名,而i是一个整数)
其次,auto一般会忽略掉顶层const,同时底层const则会保留下来,比如当初始值是一个指向常量的指针时:
const int ci = i , &cr = ci; auto b = ci; auto c = cr; auto d = &i; auto e = &ci; const auto f = ci;
上述声明中,b由于忽略掉了ci的顶层const特性所以是一个整数,c亦然,d是一个整形指针,而e则由于对常量对象取地址所以是一个底层const,从而是一个指向整形常量的指针。
如果我们希望推断出的auto类型是一个顶层const,那么需要明确指出就像f的声明一样。
还可以将引用的类型设为auto,此时原来的初始化规则仍然适用:
auto &g = ci; //g是一个整型常量引用,绑定到ci auto &h = 42; //错误:不能为非常量引用绑定字面值 const auto &j = 42; //正确:可以为常量引用绑定字面值
设置一个类型为auto的引用是,初始值中的顶层常量属性仍然保留。和往常一样,如果我们给初始值绑定一个引用,则此时的常量就不是顶层常量了。
在使用new来进行动态分配时也可以使用auto:
auto p1 = new auto(obj);
此时,p指向一个与obj类型相同的对象,该对象用obj进行初始化。注意,由于编译器要用初始化器的类型来推断要分配的类型,只有当括号中仅有单一初始化器时才可以使用auto。
unordered_map无序容器
在C++11新标准中一共定义了4个无序关联容器(unordered associative container)。这些容器不是使用比较运算符来组织元素,而是使用哈希函数(Hash Function)和关键字类型的==运算符。
4个无序关联容器如下(这个只敲了一半靠编译器提示的屑作者):
由于其名字中带有“无序(unordered)”二字,那么从表面上我们就可以看出来其使用的范围:在某关键字类型的元素没有明显的序关系的情况下。在《C++ Prime》中指出:
如果关键字类型固有就是无序的,或者性能测试发现问题可以用哈希技术解决,就可以使用无序容器
而在基础操作方面,以此处的unordered_map为例,提供了与map相同的操作,也就是说曾用于map的操作可以用于unordered_map上,而与map唯一的区别,就在于其输出也是无序的。