我正在尝试验证字符串(string_view)是否是“允许的”组合之一。
字符串是 always 3 个 ASCII 字符 - 大写和小写 - A、B、C。这些是 3 个元素的排列。
2个元素的功能相同,以后可以有4个元素的版本。
validate
的输入字符串是用户指定的。它可以是任何东西。
目前正在考虑这样的事情:
constexpr uint32_t s2u(std::string_view s){
using T = uint32_t;
return
(T(s[0]) << 8 * 0) |
(T(s[1]) << 8 * 1) |
(T(s[2]) << 8 * 2) ;
}
bool validate(std::string_view s){
if (s.size() != 3)
return false;
switch( s2u(s) ){
case s2u("abc") : case s2u("ABC") :
case s2u("acb") : case s2u("ACB") :
case s2u("bac") : case s2u("BAC") :
case s2u("bca") : case s2u("BCA") :
case s2u("cab") : case s2u("CAB") :
case s2u("cba") : case s2u("CBA") : return true;
default:
return false;
}
}
有没有更快的方法?
C 解决方案也受欢迎。
这是我更新的代码。我还决定将返回类型更改为 string_view 以便始终变为大写。
这在快速 C++ 基准测试中似乎也快得多。
我不关心字节序,因为我不需要大字节序和小字节序函数来生成相同的代码。我只关心回报是否正确。
constexpr static auto s2u(std::string_view s){
using T = uint32_t;
auto _ = [s](uint8_t i){
char const up = 0x20;
return T(s[i] | up) << 8 * i;
};
return _(0) | _(1) | _(2);
}
std::string_view validateIndex(std::string_view s){
if (s.size() != 3)
return "";
switch( s2u(s) ){
case s2u("ABC") : return "ABC";
case s2u("ACB") : return "ACB";
case s2u("BAC") : return "BAC";
case s2u("BCA") : return "BCA";
case s2u("CAB") : return "CAB";
case s2u("CBA") : return "CBA";
default:
return "";
}
}
有些肮脏但可能更快的方法是预先计算一个大小为
128
(ASCII 表大小)的数组,该数组用零填充,除了与必须具有非零唯一值的允许字符相对应的索引值。
是的...这是一张没有地图的地图:D
然后您可以根据允许序列的数组值使用基于总和的哈希,并与
std::string_view
元素进行比较。
例如:
namespace
{
constexpr int hmap[128] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
2,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,5,
6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
}
bool validate(std::string_view sv)
{
if(sv.size() != 3)
return false;
int val = hmap[static_cast<int>(sv[0])]
+ hmap[static_cast<int>(sv[1])]
+ hmap[static_cast<int>(sv[2])];
return (val == 6) || (val == 15);
}
一种更具可读性的方法可能是创建一个初始化为零的数组,并仅在
main()
的开头分配 6 个有用的值。