Looking at the code, it seems like you are reinventing the wheel.
See the answer of @Josiah for some alternatives to use the standard library.
Assuming this ain't possible for some reason, lets say its implemented badly, I would improve some smaller things.
Before giving some improvements, make sure to measure!
Is this code only used once? Does it only take 1% of the time? Is the example code the only use? Than don't spend time on this!
Currently your function accepts an empty string.
When this is encountered, you always get an error message.
If you somewhere want to handle this differently, it should be checked twice.
Or in case you know it can't be empty, it still is checked when not needed.
Instead, you could write:
assert(!str.empty() && "An empty string should not be passed");
If your specific example, you'll have to write the check in the main function. However, in that case, you could use an alternative way of handling it. For example:
std::cout << "Enter string :\n";
} while (str.empty());
std::string can have short string optimization. Although, for large strings, this might allocate memory. If you append character by character, this might reallocate.
To prevent this reallocation, you can reserve the string:
last_word.reserve(str.size() + 1 /*Null terminator*/);
The code above would be wonderful for a vector, when you have at least 1 element. However, as we have short string optimization, this might allocate, while the result won't require it.
last_word.reserve(len - j); // To be verified with null terminator.
If really performance critical, you might want to check the implementation of the standard library as they are allowed to reserve more characters than you pass. So, they can add 1 for the null terminator. (libstdc++ and MSVC do so, I read)
In order to not recreate a string, you can manipulate the original string.
erase method, you can (in bulk) remove all previous characters at once.
This will work when you don't need that argument any more, however, this might add unwanted overhead if you do or don't have a std::string instance.
std::string_view was added in a recent c++ version, this will behave as a string, although, doesn't store the content in it.
Returning a string_view might prevent copying over the characters into the
std::string. Same can be said for the input argument.
Warning: This is error prone in case you work with temporaries.