首先,停止复制东西:
template<class T>
struct array_view {
T* b = 0;
T* e = 0;
T* begin()const{return b;}
T* end()const{return e;}
// defaults:
array_view()=default;
array_view(array_view const&)=default;
array_view& operator=(array_view const&)=default;
~array_view()=default;
array_view( T* s, size_t n ):array_view(s, s+n){}
array_view( T* s, T* f ):b(s),e(f){}
using mutable_T = typename std::remove_const<T>::type;
template<size_t N>
array_view( T(&arr)[N] ):array_view(arr, N){}
template<size_t N>
array_view( std::array<T,N>&arr ):array_view(arr.data(), N){}
template<size_t N>
array_view( std::array<mutable_T,N>const&arr ):array_view(arr.data(), N){}
// similar for std::vector:
template<class...Ts>
array_view( std::basic_string<mutable_T, Ts...> const& src):
array_view(src.data(), src.size())
{}
template<class...Ts>
array_view( std::basic_string<T, Ts...>& src):
array_view(
src.empty()?
array_view():
array_view(std::addressof(src[0]),src.size())
)
{}
T& back() const { return *std::prev(end()); }
T& front() const { return *begin(); }
size_t size() const { return end()-begin(); }
bool empty() const { return begin()==end(); }
// slicing functions:
array_view front( size_t n ) const {
if (size() <= n) return *this;
return {begin(), n};
}
array_view back( size_t n ) const {
if (size() <= n) return *this;
return {end()-n, n};
}
array_view trim_front( size_t n ) const {
return back( size()-n );
}
array_view trim_back( size_t n ) const {
return front( size()-n );
}
array_view sub( size_t start, size_t len ) const {
if (start >= size()) return {};
len = (std::min)( size()-start, len );
return {begin()+start, len};
}
// comparisons:
friend bool operator==( array_view lhs, array_view rhs ) {
if (lhs.size()!=rhs.size()) return false;
return std::equal( lhs.begin(), lhs.end(), rhs.begin() );
}
friend bool operator!=( array_view lhs, array_view rhs ) {
return !(lhs==rhs);
}
friend bool operator<( array_view lhs, array_view rhs ) {
return std::lexicographical_compare(
lhs.begin(), lhs.end(),
rhs.begin(), rhs.end()
);
}
friend bool operator>( array_view lhs, array_view rhs ) {
return rhs<lhs;
}
friend bool operator<=( array_view lhs, array_view rhs ) {
return !(lhs>rhs);
}
friend bool operator>=( array_view lhs, array_view rhs ) {
return !(lhs<rhs);
}
};
anarray_view
是一个没有拥有的范围。它不支持 char 特征,但我不太在意。
using string_view = array_view<const char>;
size_t common_prefix( string_view lhs, string_view rhs ) {
auto itl = lhs.begin();
auto itr = rhs.begin();
while (itl != lhs.end() && itr != rhs.end()) {
if (*itl != *itr) break;
++itl; ++itr;
}
return itl-lhs.begin();
}
lhs
给了我们and的最长公共前缀rhs
。
现在我们要做的就是快速有效地识别YZ
vs。ZY
bool is_yz_zy( string_view lhs, string_view rhs ) {
if (lhs.size() < 2) return false;
if (lhs.size() != rhs.size()) return false;
for (size_t i = 1; i < lhs.size(); ++i) {
if (lhs.front(i)==rhs.back(i)) {
if (lhs.trim_front(i) == rhs.trim_back(i)) {
return true;
}
}
}
return false;
}
并缝合:
bool is_xyz_xzy( string_view lhs, string_view rhs ) {
size_t max_x = common_prefix(lhs, rhs);
for (size_t i = 0; i <= max_x; ++i) {
if (is_yz_zy( lhs.trim_front(i), rhs.trim_front(i) ))
return true;
}
return false;
}
使用 O(1) 内存。
活生生的例子
优化时间。
进行异或扫描。现在唯一x
可能的长度是那些在该索引处异或扫描相等的长度,并且整个字符串的异或扫描相等。
类似地,对于 yz zy 检测,索引 i 处的左 xor 扫描必须等于索引长度处的右 xor 扫描 - i xor 右 xor 扫描长度为 i 的长度为 y。
具有仍然友好属性的更强大的哈希将使病理情况不那么明显,但异或扫描应该有很大帮助。
异或扫描是所有先前字符的异或。这可以在字符串中就地完成,将每个字符替换为自身的 xor 和所有先前的字符。该操作很容易反转,并且需要线性时间。
字符串比较需要一点小心,但您只需将每个扫描条目与前一次扫描进行异或即可获得原始字符。
这是异或优化版本的实现。 实验上它做了 ~5 n^2
字符操作,但它会有 n^3 的情况。