instead of saying "(either not this | or not that)", you need to say "not (either this | or that)". Currently, if either condition is true, a match is made. A "p" in an html tag isn't in an entity, and vice versa, so of course it matches.
...
Here you go:
Code:
p(?!([^<&]+)?(>|;))
First we search for the "p" then we check ahead to see if the following is
not there: any character string that does NOT contain
either a "<" or "&" ,
followed by either a ">" or ";"
EDIT: If your sentence ends in a ";" this won't match, (you can try adding a negative lookbehind for a "&" and seeing where that leads you) so you're probably better off using calibre to convert all entities to unicode. And tags won't have that problem.
EDIT #2: added regex pipe and parentheses to beginning of answer, to clarify how it works.