The first assertion is guaranteed to succeed. c_str() always returns a pointer to a null-terminated string with the same string contents as held by the std::string object, which is an empty string for both s1.
The second assertion is not guaranteed to succeed. There is nothing requiring the c_str() returned from a std::string to be the same if the content is the same. Default-constructed strings do not need to share the same underlying buffer. That would be an implementation detail of a particular standard library implementation. (I think libstdc++ does something like this depending on configuration for some backwards-compatibility(?) reasons if I remember correctly, see the --enable-fully-dynamic-string configure option).
Note that prior to C++11, data() did not have the same effect as c_str(). data() was not guaranteed to give a pointer to a null-terminated string. If the string was empty, then the pointer returned by it was not allowed to be dereferenced. So replacing c_str() with data() in your examples would, prior to C++11, result in undefined behavior on the call to strlen.
The wording "and can have 0 added to it" is somewhat weird and I am not completely sure what it is supposed to convey, but for C++11 (draft N3337) data()'s return value is further specified in [string.accessors]/1 so that data() + i == &operator[](i) for all i in the range [0,size()] and operator[] is specified in [strings.access]/2 to return a reference to a CharT() (aka a null character) for operator[](size()) without any conditions.
The strange wording has also been replaced via editorial change in 2018, see https://github.com/cplusplus/draft/pull/1879.